Cannot detect collisions in spritekit - swift

I am trying to build a game and my physics body contacts are not being detected. Can you pls help me? I have set the physicsWorldDelegate and categorybitmasks but cannot figure out what is wrong. The playground couldn't detect the collisions between the enemy and my avatar.
import SpriteKit
public class GameScene: SKScene, SKPhysicsContactDelegate {
let coronaSpeed: CGFloat = 80.0
let playerspeed: CGFloat = 155.0
var mask: SKSpriteNode?
var player: SKSpriteNode?
var mcorona: [SKSpriteNode] = []
var lastTouch: CGPoint? = nil
override public func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
// Animations
player = childNode(withName: "player") as? SKSpriteNode
mask = childNode(withName: "mask") as? SKSpriteNode
mask!.run(SKAction.repeatForever(
SKAction.sequence([
SKAction.moveBy(x: 0, y: 10, duration: 0.45),
SKAction.moveBy(x: 0, y: -10, duration: 0.45)
]
)))
for child in self.children {
if child.name == "corona" {
if let child = child as? SKSpriteNode {
mcorona.append(child)
}
}
}
// </> Animations
}
override public func touchesBegan(_ touches: Set<UITouch>,with event: UIEvent?) { handleTouches(touches)
updatePlayer()
updateZombies()
}
override public func touchesMoved(_ touches: Set<UITouch>,with event: UIEvent?) { handleTouches(touches)
}
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { handleTouches(touches) }
fileprivate func handleTouches(_ touches: Set<UITouch>) { lastTouch = touches.first?.location(in: self) }
override public func didSimulatePhysics() {
if player != nil {
updatePlayer()
updateZombies()
}
}
fileprivate func shouldMove(currentPosition: CGPoint,
touchPosition: CGPoint) -> Bool {
guard let player = player else { return false }
return abs(currentPosition.x - touchPosition.x) > player.frame.width / 2 ||
abs(currentPosition.y - touchPosition.y) > player.frame.height / 2
}
fileprivate func updatePlayer() {
guard let player = player,
let touch = lastTouch
else { return }
let currentPosition = player.position
if shouldMove(currentPosition: currentPosition,
touchPosition: touch) {
updatePosition(for: player, to: touch, speed: playerspeed)
} else {
player.physicsBody?.isResting = true
}
}
func updateZombies() {
guard let player = player else { return }
let targetPosition = player.position
for corona in mcorona {
updatePosition(for: corona, to: targetPosition, speed: coronaSpeed)
}
}
func getDuration(pointA:CGPoint,pointB:CGPoint,speed:CGFloat)->TimeInterval{
let xDist = (pointB.x - pointA.x)
let yDist = (pointB.y - pointA.y)
let distance = sqrt((xDist * xDist) + (yDist * yDist));
let duration : TimeInterval = TimeInterval(distance/speed)
return duration
}
fileprivate func updatePosition(for sprite: SKSpriteNode, to target: CGPoint, speed: CGFloat) {
let currentPosition = sprite.position
let angle = CGFloat.pi + atan2(currentPosition.y - target.y, currentPosition.x - target.x)
let rotateAction = SKAction.rotate(toAngle: angle + (CGFloat.pi*0.5), duration: 0)
sprite.run(rotateAction)
//sprite.physicsBody?.isDynamic = true
let velocityX = speed * cos(angle)
let velocityY = speed * sin(angle)
let newVelocity = CGVector(dx: velocityX, dy: velocityY)
sprite.physicsBody?.velocity = newVelocity
sprite.physicsBody?.affectedByGravity = false
let moveToTouch = SKAction.move(to: CGPoint(x: target.x, y: target.y),duration: getDuration(pointA:currentPosition,pointB:target,speed:speed))
sprite.run(moveToTouch)
}
public func didBegin(_ contact: SKPhysicsContact) {
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
// Check contact
if firstBody.categoryBitMask == player?.physicsBody?.categoryBitMask &&
secondBody.categoryBitMask == mcorona[0].physicsBody?.categoryBitMask {
// Player & corona
gameOver(false)
} else if firstBody.categoryBitMask == player?.physicsBody?.categoryBitMask &&
secondBody.categoryBitMask == mask?.physicsBody?.categoryBitMask {
// Player & mask
gameOver(true)
}
}
fileprivate func gameOver(_ didWin: Bool) {
let resultScene = MenuScene(size: size, didWin: didWin, levelToSend: 2)
let transition = SKTransition.flipVertical(withDuration: 1.0)
view?.presentScene(resultScene, transition: transition)
}
}
Here is a GIF to my problem-
https://giphy.com/gifs/U7nmXZgVxNcCuvJc7j

Related

SKSpriteNode and SKAction with animating sprite

Im trying to get the player sprite in my game to animate according to what it is doing, ie.. walking, attacking, resting.
I have the following files AgentNode and GameScene.
The sprite spawns and moves around as I want, just with the initial action that was in place when the sprite was added to the scene.
for example if I add the sprite with the "restingFrames" animation, it spawns correctly but when the sprite is moved, the animation is still using "restingFrames" and not "walkingFrames" as I want. Can't figure it out, please help.
AgentNode.swift
import SpriteKit
import GameplayKit
class AgentNode: SKNode, GKAgentDelegate {
var agent = GKAgent2D()
var triangleShape = SKShapeNode()
var player = SKSpriteNode()
var walkingFrames: [SKTexture] = []
var restingFrames: [SKTexture] = []
var attackingFrames: [SKTexture] = []
var firstFrameTexture: SKTexture = SKTexture()
var playerSpawned = false
override init() {
super.init()
}
init(scene:SKScene, radius: Float, position: CGPoint) {
super.init()
self.position = position
self.zPosition = 10
scene.addChild(self)
agent.radius = radius
agent.position = simd_float2(Float(position.x), Float(position.y))
agent.delegate = self
agent.maxSpeed = 100 * 2
agent.maxAcceleration = 500 * 4
}
func setupPlayer() {
setupRestingPlayerAnimation()
setupWalkingPlayerAnimation()
setupAttackingPlayerAnimation()
}
func setupWalkingPlayerAnimation() {
let walkingPlayerAtlas = SKTextureAtlas(named: "WalkingPlayer")
let numImages = walkingPlayerAtlas.textureNames.count
for i in 1...numImages {
let walkingPlayerTextureName = "walk_front\(i)"
walkingFrames.append(walkingPlayerAtlas.textureNamed(walkingPlayerTextureName))
}
}
func setupRestingPlayerAnimation() {
let restingPlayerAtlas = SKTextureAtlas(named: "RestingPlayer")
let numImages = restingPlayerAtlas.textureNames.count
for i in 1...numImages {
let restingPlayerTextureName = "still_frame\(i)"
restingFrames.append(restingPlayerAtlas.textureNamed(restingPlayerTextureName))
}
}
func setupAttackingPlayerAnimation() {
let attackingPlayerAtlas = SKTextureAtlas(named: "AttackingPlayer")
let numImages = attackingPlayerAtlas.textureNames.count
for i in 1...numImages {
let attackingPlayerTextureName = "attack_frame\(i)"
attackingFrames.append(attackingPlayerAtlas.textureNamed(attackingPlayerTextureName))
}
}
func animatePlayer() {
var restingSequence = SKAction()
var walkingSequence = SKAction()
var attackingSequence = SKAction()
let restingAnimation = SKAction.animate(with: restingFrames, timePerFrame: 0.15)
let walkingAnimation = SKAction.animate(with: walkingFrames, timePerFrame: 0.1)
let attackingAnimation = SKAction.animate(with: attackingFrames, timePerFrame: 0.1)
restingSequence = SKAction.sequence([restingSequence])
walkingSequence = SKAction.sequence([walkingSequence])
attackingSequence = SKAction.sequence([attackingSequence])
if isSeeking && !isAttacking{
firstFrameTexture = walkingFrames[0]
player.run(SKAction.repeatForever(walkingAnimation), withKey: "walkingAction")
print("walking")
} else if isAttacking && !isSeeking {
firstFrameTexture = attackingFrames[0]
player.run(SKAction.repeatForever(attackingAnimation), withKey: "attackingAction")
print("attacking")
} else {
firstFrameTexture = restingFrames[0]
player.run(SKAction.repeatForever(restingAnimation), withKey: "restingAction")
print("resting")
}
}
func addPlayerToScene() {
player = SKSpriteNode(texture: firstFrameTexture)
player.position = CGPoint(x: (frame.midX), y: (frame.midY))
player.setScale(1.5)
player.zRotation = CGFloat(Double.pi / 2.0)
player.zPosition = 10
self.addChild(player)
playerSpawned = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func agentWillUpdate(_ agent: GKAgent) {
}
func agentDidUpdate(_ agent: GKAgent) {
if playerSpawned == false {
print("player has not spawned")
setupPlayer()
animatePlayer()
addPlayerToScene()
playerSpawned = true
} else {
print("player has spawned")
animatePlayer()
}
print("player isSeeking \(isSeeking)")
print("player isAttacking \(isAttacking)")
guard let agent2D = agent as? GKAgent2D else {
return
}
self.position = CGPoint(x: CGFloat(agent2D.position.x), y: CGFloat(agent2D.position.y))
self.zRotation = CGFloat(agent2D.rotation)
}
}
GameScene.swift
import SpriteKit
import GameplayKit
var isSeeking: Bool = false
var isAttacking: Bool = false
class GameScene: SKScene {
let trackingAgent = GKAgent2D()
var player = AgentNode()
var seekGoal : GKGoal = GKGoal()
let stopGoal = GKGoal(toReachTargetSpeed: 0.0)
var seeking : Bool = false {
willSet {
if newValue {
self.player.agent.behavior?.setWeight(5, for: seekGoal)
self.player.agent.behavior?.setWeight(0, for: stopGoal)
} else {
self.player.agent.behavior?.setWeight(0, for: seekGoal)
self.player.agent.behavior?.setWeight(5, for: stopGoal)
}
}
}
var agentSystem = GKComponentSystem()
var lastUpdateTime: TimeInterval = 0
override func didMove(to view: SKView) {
super.didMove(to: view)
self.trackingAgent.position = simd_float2(Float(self.frame.midX), Float(self.frame.midY))
self.agentSystem = GKComponentSystem(componentClass: GKAgent2D.self)
self.player = AgentNode(scene: self, radius: Float(20.0), position: CGPoint(x: self.frame.midX, y: self.frame.midY))
self.player.agent.behavior = GKBehavior()
self.agentSystem.addComponent(self.player.agent)
self.seekGoal = GKGoal(toSeekAgent: self.trackingAgent)
}
override func update(_ currentTime: CFTimeInterval) {
isSeeking = self.seeking
if lastUpdateTime == 0 {
lastUpdateTime = currentTime
}
let delta = currentTime - lastUpdateTime
lastUpdateTime = currentTime
self.agentSystem.update(deltaTime: delta)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.seeking = true
handleTouch(touches: touches)
}
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
self.seeking = false
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.seeking = false
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches: touches)
}
func handleTouch(touches:Set<UITouch>) {
guard let touch = touches.first
else {
return
}
let location = touch.location(in: self)
self.trackingAgent.position = simd_float2(Float(location.x), Float(location.y))
}
}
Well I figured it out. I updated the code as follows and everything is working great.
AgentNode.swift
import SpriteKit
import GameplayKit
class AgentNode: SKNode, GKAgentDelegate {
var agent = GKAgent2D()
var triangleShape = SKShapeNode()
var player = SKSpriteNode()
var walkingFrames: [SKTexture] = []
var restingFrames: [SKTexture] = []
var attackingFrames: [SKTexture] = []
var playerSpawned = false
override init() {
super.init()
}
init(scene:SKScene, radius: Float, position: CGPoint) {
super.init()
self.position = position
self.zPosition = 10
scene.addChild(self)
agent.radius = radius
agent.position = simd_float2(Float(position.x), Float(position.y))
agent.delegate = self
agent.maxSpeed = 100 * 2
agent.maxAcceleration = 500 * 4
}
func setupPlayer() {
setupRestingPlayerAnimation()
setupWalkingPlayerAnimation()
setupAttackingPlayerAnimation()
}
func setupWalkingPlayerAnimation() {
let walkingPlayerAtlas = SKTextureAtlas(named: "WalkingPlayer")
let numImages = walkingPlayerAtlas.textureNames.count
for i in 1...numImages {
let walkingPlayerTextureName = "walk_front\(i)"
walkingFrames.append(walkingPlayerAtlas.textureNamed(walkingPlayerTextureName))
}
}
func setupRestingPlayerAnimation() {
let restingPlayerAtlas = SKTextureAtlas(named: "RestingPlayer")
let numImages = restingPlayerAtlas.textureNames.count
for i in 1...numImages {
let restingPlayerTextureName = "still_frame\(i)"
restingFrames.append(restingPlayerAtlas.textureNamed(restingPlayerTextureName))
}
}
func setupAttackingPlayerAnimation() {
let attackingPlayerAtlas = SKTextureAtlas(named: "AttackingPlayer")
let numImages = attackingPlayerAtlas.textureNames.count
for i in 1...numImages {
let attackingPlayerTextureName = "attack_frame\(i)"
attackingFrames.append(attackingPlayerAtlas.textureNamed(attackingPlayerTextureName))
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func agentWillUpdate(_ agent: GKAgent) {
}
func agentDidUpdate(_ agent: GKAgent) {
guard let agent2D = agent as? GKAgent2D else {
return
}
self.position = CGPoint(x: CGFloat(agent2D.position.x), y: CGFloat(agent2D.position.y))
self.zRotation = CGFloat(agent2D.rotation)
}
}
GameScene.swift
import SpriteKit
import GameplayKit
var isSeeking: Bool = false
var isAttacking: Bool = false
class GameScene: SKScene {
let trackingAgent = GKAgent2D()
var player = AgentNode()
var seekGoal : GKGoal = GKGoal()
let stopGoal = GKGoal(toReachTargetSpeed: 0.0)
var seeking : Bool = false {
willSet {
if newValue {
self.player.agent.behavior?.setWeight(5, for: seekGoal)
self.player.agent.behavior?.setWeight(0, for: stopGoal)
} else {
self.player.agent.behavior?.setWeight(0, for: seekGoal)
self.player.agent.behavior?.setWeight(5, for: stopGoal)
}
}
}
var agentSystem = GKComponentSystem()
var lastUpdateTime: TimeInterval = 0
override func didMove(to view: SKView) {
super.didMove(to: view)
self.trackingAgent.position = simd_float2(Float(self.frame.midX), Float(self.frame.midY))
self.agentSystem = GKComponentSystem(componentClass: GKAgent2D.self)
self.player = AgentNode(scene: self, radius: Float(20.0), position: CGPoint(x: self.frame.midX, y: self.frame.midY))
self.player.agent.behavior = GKBehavior()
self.agentSystem.addComponent(self.player.agent)
self.seekGoal = GKGoal(toSeekAgent: self.trackingAgent)
player.setupPlayer()
//player.animatePlayer()
player.player = SKSpriteNode(imageNamed: "default_pose")
player.player.position = CGPoint(x: (frame.midX), y: (frame.midY))
player.player.setScale(1.5)
player.player.zRotation = CGFloat(Double.pi / 2.0)
player.player.zPosition = 10
player.player.run(SKAction.repeatForever(SKAction.animate(with: player.restingFrames, timePerFrame: 0.2)))
player.addChild(player.player)
}
override func update(_ currentTime: CFTimeInterval) {
isSeeking = self.seeking
if lastUpdateTime == 0 {
lastUpdateTime = currentTime
}
let delta = currentTime - lastUpdateTime
lastUpdateTime = currentTime
self.agentSystem.update(deltaTime: delta)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.seeking = true
player.player.run(SKAction.repeatForever(SKAction.animate(with: player.walkingFrames, timePerFrame: 0.1)))
handleTouch(touches: touches)
}
override func touchesCancelled(_ touches: Set<UITouch>?, with event: UIEvent?) {
self.seeking = false
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
self.seeking = false
player.player.removeAllActions()
player.player.run(SKAction.repeatForever(SKAction.animate(with: player.attackingFrames, timePerFrame: 0.1)))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
handleTouch(touches: touches)
}
func handleTouch(touches:Set<UITouch>) {
guard let touch = touches.first
else {
return
}
let location = touch.location(in: self)
self.trackingAgent.position = simd_float2(Float(location.x), Float(location.y))
}
}

Nodes are colliding but not responding to didBeginContact function

My nodes are colliding when i run it. The coin bounces of the player node. However, when I want to call the didBeginContact function it is not responding...
I want eventually a label to display the score +1 every time the coin hits the player. Also the coin should disappear when colliding with the player. But my contact is not working so I can't make any collision rules make the label display the score.
import SpriteKit
import GameplayKit
// Collision categories
struct physicsCategory {
static let playerCat : UInt32 = 1
static let coinCat : UInt32 = 2
}
class GameScene: SKScene, controls, SKPhysicsContactDelegate {
let player = SKSpriteNode(imageNamed:"trump")
let points = SKLabelNode()
let buttonDirLeft = SKSpriteNode(imageNamed: "left")
let buttonDirRight = SKSpriteNode(imageNamed: "right")
let background = SKSpriteNode(imageNamed: "background")
var pressedButtons = [SKSpriteNode]()
let popUpMenu = SKSpriteNode(imageNamed: "popupmenu")
var score = 0
var gameOver = false
var startGame = false
var rules = false
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
//score label
points.position = CGPoint(x: 530, y: 260)
points.text = ("\(score)")
points.zPosition = 6
points.fontColor = UIColor.black
points.fontSize = 50
addChild(points)
//Set Background
background.zPosition = 1
background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
background.size.width = 580
background.size.height = 320
addChild(background)
// Player
player.position = CGPoint(x: 250, y: 40)
player.zPosition = 2
player.size.width = 40
player.size.height = 60
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
player.physicsBody?.affectedByGravity = false
player.physicsBody!.categoryBitMask = physicsCategory.playerCat
player.physicsBody!.contactTestBitMask = physicsCategory.coinCat
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.isDynamic = false
self.addChild(player)
//contact has started
func didBeginContact(contact: SKPhysicsContact){
let firstBody: SKPhysicsBody = contact.bodyA
let secondBody: SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == physicsCategory.playerCat) && (secondBody.categoryBitMask == physicsCategory.coinCat)){
CollisionWithCoin(player: firstBody.node as! SKSpriteNode, coins: secondBody.node as! SKSpriteNode)
}
}
func CollisionWithCoin(player: SKSpriteNode, coins:SKSpriteNode){
NSLog("Hello")
}
//repeat coing spawning
run(SKAction.repeatForever(
SKAction.sequence([
SKAction.run(spawnCoins),
SKAction.wait(forDuration: 1.0)])))
}
//coin settings
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
//spawn coins
func spawnCoins() {
// 2
let coins = SKSpriteNode(imageNamed: "coins")
coins.zPosition = 2
coins.size.width = 25
coins.size.height = 25
coins.physicsBody = SKPhysicsBody(rectangleOf: coins.size )
coins.physicsBody!.categoryBitMask = physicsCategory.coinCat
coins.physicsBody!.contactTestBitMask = physicsCategory.playerCat
coins.physicsBody?.collisionBitMask = 1
coins.position = CGPoint(x: frame.size.width * random(min: 0, max: 1), y: frame.size.height + coins.size.height/2)
let action = SKAction.moveTo(y: -350, duration: TimeInterval(random(min: 1, max: 5)))
let remove = SKAction.run({coins.removeFromParent()})
let sequence = SKAction.sequence([action,remove])
coins.run(sequence)
addChild(coins)
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
/* Called before each frame is rendered */
if pressedButtons.index(of: buttonDirLeft) != nil {
player.position.x -= 4.0
}
if pressedButtons.index(of: buttonDirRight) != nil {
player.position.x += 4.0
}
}
//MOVEMENT FUNCTIONS START HERE
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
for button in [buttonDirLeft, buttonDirRight] {
// I check if they are already registered in the list
if button.contains(location) && pressedButtons.index(of: button) == nil {
pressedButtons.append(button)
}
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
for button in [buttonDirLeft, buttonDirRight] {
// if I get off the button where my finger was before
if button.contains(previousLocation)
&& !button.contains(location) {
// I remove it from the list
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
// if I get on the button where I wasn't previously
else if !button.contains(previousLocation)
&& button.contains(location)
&& pressedButtons.index(of: button) == nil {
// I add it to the list
pressedButtons.append(button)
}}}}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
for button in [buttonDirLeft, buttonDirRight] {
if button.contains(location) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
else if (button.contains(previousLocation)) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
}
}
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let location = touch.location(in: self)
let previousLocation = touch.previousLocation(in: self)
for button in [buttonDirLeft, buttonDirRight] {
if button.contains(location) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
else if (button.contains(previousLocation)) {
let index = pressedButtons.index(of: button)
if index != nil {
pressedButtons.remove(at: index!)
}
}
}
}
}
}
Can you try defining your bit masks like this.
enum PhysicsCategory {
static let playerCat: UInt32 = 0x1 << 0
static let coinCat: UInt32 = 0x1 << 1
}
and can you try this code in your contact method. Also note that if you are using Swift 3 the name of the contact method name has changed.
//contact has started
func didBegin(_ contact: SKPhysicsContact) {
let firstBody: SKPhysicsBody = contact.bodyA
let secondBody: SKPhysicsBody = contact.bodyB
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask == physicsCategory.playerCat) && (secondBody.categoryBitMask == physicsCategory.coinCat)){
CollisionWithCoin(player: firstBody.node as! SKSpriteNode, coins: secondBody.node as! SKSpriteNode)
}
}
}
You are also using a few ! in your code which makes it less safe. Try using ? and "if let" whenever possible when dealing with optionals. So for example write your physics bodies like this even though you know you just created it. You are doing it sometimes and other times you are using !, be consistent.
player.physicsBody?.categoryBitMask...
etc
If that physics body for some reason is/becomes nil and you are using ! you will crash.
I would also write your contact method like this, to ensure you also dont crash if the contact methods fires more than once for the same collision.
func collisionWithCoin(player: SKSpriteNode?, coins:SKSpriteNode?){
guard let player = player, let coins = coins else { return }
print("Hello")
}
and than call it like so in the didBeginContact method
collisionWithCoin(player: firstBody.node as? SKSpriteNode, coins: secondBody.node as? SKSpriteNode)
Finally I would also try to follow the swift guidelines, your methods should start with small letters and classes, structs should start with capital letters.
Hope this helps

Sprite kit contact problems

I am making a game that requires you to dodge falling objects. I have tried to make it it so that when the falling object hits the player certain code runs. But it is not working please show me what is wrong with my code.
import SpriteKit
struct physicsCatagory {
static let person : UInt32 = 0x1 << 1
static let Ice : UInt32 = 0x1 << 2
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var person = SKSpriteNode(imageNamed: "Person")
override func didMoveToView(view: SKView) {
physicsWorld.contactDelegate = self
person.position = CGPointMake(self.size.width/2, self.size.height/12)
person.setScale(0.4)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.categoryBitMask = physicsCatagory.person
person.physicsBody?.contactTestBitMask = physicsCatagory.Ice
person.physicsBody?.dynamic = false
var IceTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("spawnFirstIce"), userInfo: nil, repeats: true)
self.addChild(person)
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == physicsCatagory.person && secondBody.categoryBitMask == physicsCatagory.Ice || firstBody.categoryBitMask == physicsCatagory.Ice && secondBody.categoryBitMask == physicsCatagory.person{
NSLog ("Test Test") }
}
}
func spawnFirstIce(){
let Ice = SKSpriteNode(imageNamed: "FirstIce")
Ice.setScale(0.5)
Ice.physicsBody = SKPhysicsBody(rectangleOfSize: Ice.size)
Ice.physicsBody?.categoryBitMask = physicsCatagory.Ice
Ice.physicsBody?.contactTestBitMask = physicsCatagory.person
Ice.physicsBody?.affectedByGravity = false
Ice.physicsBody?.dynamic = true
let MinValue = self.size.width / 8
let MaxValue = self.size.width - 20
let SpawnPoint = UInt32(MaxValue - MinValue)
Ice.position = CGPoint(x: CGFloat(arc4random_uniform(SpawnPoint)), y: self.size.height)
self.addChild(Ice)
let action = SKAction.moveToY(-85, duration: 3.0)
let actionDone = SKAction.removeFromParent()
Ice.runAction(SKAction.sequence([action,actionDone]))
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
person.position.x = location.x
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
person.position.x = location.x
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
You have defined didBeginContact inside of didMoveToView method :)
Place didBeginContact outside of that method and make it a member of GameScene class.

Spritekit - Swift/ Multiple collisions. I'm having trouble with putting the cell and the bacteria in the same function

I'm having trouble getting the bacteria and the cell below together inside the didBeginContact function. It seems like I have to separate them from each other for them to have different outcomes when they collide with objects instead of getting mixed up together.
let virusMask:UInt32 = 0x1 << 0
let cellMask:UInt32 = 0x1 << 1
let finishMask:UInt32 = 0x1 << 2
let wallMask:UInt32 = 0x1 << 3
class GameScene: SKScene, SKPhysicsContactDelegate {
var touchLocation: CGPoint = CGPointZero
var boneCannon: SKSpriteNode!
var smoke: SKEmitterNode!
var Finish: SKSpriteNode!
var bacteria: SKScene!
override func didMoveToView(view: SKView) {
boneCannon = self.childNodeWithName("boneCannon") as! SKSpriteNode
Finish = self.childNodeWithName("FinishLine") as! SKSpriteNode
let bacteria:SKSpriteNode = SKScene(fileNamed: "bacterioP")!.childNodeWithName("bacteriophage") as! SKSpriteNode
bacteria.removeFromParent()
self.addChild(bacteria)
bacteria.zPosition = 2
bacteria.position = CGPointMake(600, 600)
bacteria.runAction(SKAction.moveToY(1920, duration: 5))
bacteria.physicsBody?.collisionBitMask = wallMask|cellMask|virusMask
bacteria.physicsBody?.contactTestBitMask = bacteria.physicsBody!.collisionBitMask | virusMask
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.smoke = SKEmitterNode(fileNamed: "FireFlies")!
self.smoke.position = CGPointMake(600, 600)
self.smoke.zPosition = 1
self.addChild(smoke)
let a1 = SKAction.moveByX(850.0, y: 0.0, duration: 0.75)
let a2 = SKAction.moveByX(-850.0, y: 0.0, duration: 0.75)
let seq = SKAction.sequence([a1,a2])
self.Finish.runAction(SKAction.repeatActionForever(seq))
self.physicsWorld.contactDelegate = self
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
touchLocation = touches.first!.locationInNode(self)
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
touchLocation = touches.first!.locationInNode(self)
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let cell: SKSpriteNode = SKScene(fileNamed: "WhiteBloodCell")!.childNodeWithName("cell") as! SKSpriteNode
cell.removeFromParent()
self.addChild(cell)
cell.zPosition = 1
cell.position = boneCannon.position
let cellRadians = Float(boneCannon.zRotation)
let speed = CGFloat(20.0)
let vx: CGFloat=CGFloat(cosf(cellRadians)) * speed
let vy: CGFloat = CGFloat(sinf(cellRadians)) * speed
cell.physicsBody?.applyImpulse(CGVectorMake(vx, vy))
cell.physicsBody?.collisionBitMask = virusMask | cellMask | wallMask
cell.physicsBody?.contactTestBitMask = cell.physicsBody!.collisionBitMask | finishMask
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let percent = touchLocation.x / size.width
let newAngle = percent * 180-180
boneCannon.zRotation = CGFloat(newAngle) * CGFloat(M_PI) / 180.0
}
func didBeginContact(contact: SKPhysicsContact) {
let cell = (contact.bodyA.collisionBitMask == cellMask) ? contact.bodyA: contact.bodyB
let other = (cell == contact.bodyA) ? contact.bodyB: contact.bodyA
if other.categoryBitMask == virusMask{
self.didHitVirus(other)
}
if other.categoryBitMask == finishMask{
let secondlevel:GameScene = GameScene(fileNamed: "SecondLevel")!
secondlevel.scaleMode = .AspectFill
self.view?.presentScene(secondlevel)
}
let bacteria = (contact.bodyA.collisionBitMask == virusMask) ? contact.bodyA: contact.bodyB
let object = (bacteria == contact.bodyA) ? contact.bodyB: contact.bodyA
if object.categoryBitMask == wallMask{
let fail:GameScene = GameScene(fileNamed: "ThirdLevel")!
fail.scaleMode = .AspectFill
self.view?.presentScene(fail)
}
func didHitVirus(virus:SKPhysicsBody){
let particleEffect: SKEmitterNode = SKEmitterNode(fileNamed: "ParticleScene")!
//if ((virus.node?.position) != nil) {}
particleEffect.position = CGPointMake(600, 600)
particleEffect.zPosition = 4;
self.addChild(particleEffect)
virus.node?.removeFromParent()
}
}

Adding multiple sprites to a screen

I am new to SpriteKit.
I started working on a game where I want a menu of sprites that I can select from. When I select a particular sprite I want to then be able to tap on the screen and produce copies of them.
Here is a copy of my code I have trying.
class GameScene: SKScene
{
override func didMoveToView(view: SKView)
{
let button1 = SKSpriteNode(imageNamed: "Monster1A.png")
button1.position = CGPointMake(500, 600)
button1.name = "Monster1A"
self.addChild(button1)
let button2 = SKSpriteNode(imageNamed: "Monster2A.png")
button2.position = CGPointMake(300, 600)
button2.name = "Monster2A"
self.addChild(button2)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
let touch = touches
let location = touch.first!.locationInNode(self)
let node = self.nodeAtPoint(location)
if (node.name == "Monster1A")
{
for touch in touches
{
let touchLocation = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Monster1A")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = touchLocation
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
self.addChild(sprite)
}
}
if (node.name == "Monster2A")
{
for touch in touches
{
let touchLocation = touch.locationInNode(self)
let sprite = SKSpriteNode(imageNamed:"Monster2A")
sprite.xScale = 0.5
sprite.yScale = 0.5
sprite.position = touchLocation
let action = SKAction.rotateByAngle(CGFloat(M_PI), duration:1)
sprite.runAction(SKAction.repeatActionForever(action))
self.addChild(sprite)
}
}
}
}
Try this :)
import SpriteKit
class GameScene: SKScene {
var isDuplicating: Bool = false
var selectedSpriteName: String!
override func didMoveToView(view: SKView) {
let button1 = SKSpriteNode(imageNamed:"Monster1A")
button1.position = CGPointMake(500, 600)
button1.name = "Monster1A"
self.addChild(button1)
let button2 = SKSpriteNode(imageNamed:"Monster2A")
button2.position = CGPointMake(300, 600)
button2.name = "Monster2A"
self.addChild(button2)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(self)
let sprite = self.nodeAtPoint(location)
if sprite.name == "Monster1A" {
if self.isDuplicating == true {
// Deselect
self.isDuplicating = false
return
}
self.selectedSpriteName = "Monster1A"
self.isDuplicating = true
} else if sprite.name == "Monster2A" {
if self.isDuplicating == true {
// Deselect
self.isDuplicating = false
return
}
self.selectedSpriteName = "Monster2A"
self.isDuplicating = true
} else {
if self.isDuplicating == true {
let newsprite = SKSpriteNode(imageNamed: self.selectedSpriteName)
newsprite.position = location
addChild(newsprite)
}
}
}
}
}