SKPhysics Contact not functioning properly - swift

I am trying to use the SKPhysicsContactDelegate function in SpriteKit and it will not seem to work. I want one sprite to perform an action when it hits the other. I have set up breakpoints at the didBeginContact function and for some reason my application never calls this function. All help appreciated. Code posted below.
struct PhysicsCatagory {
static let Enemy :UInt32 = 0x1 << 0
static let Slider :UInt32 = 0x1 << 1
static let Circle :UInt32 = 0x1 << 2
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var EnemyTimer = NSTimer()
var Circle = SKSpriteNode()
var Slider = SKSpriteNode()
var FastButton = SKNode()
var Title = SKSpriteNode()
var Text = SKSpriteNode()
var Path = UIBezierPath()
var gameStarted = Bool()
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
self.backgroundColor = UIColor.whiteColor()
Circle = SKSpriteNode(imageNamed:"blueCircle")
Circle.size = CGSize(width: 140, height: 140)
Circle.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
Circle.zPosition = 1.0
self.addChild(Circle)
Slider = SKSpriteNode(imageNamed: "blocker1")
Slider.size = CGSize(width: 15, height: 50)
Slider.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 80)
addChild(Slider)
Slider.zPosition = 1.0
moveClockWise()
}
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.node != nil && contact.bodyB.node != nil{
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
if ((firstBody.name == "Enemy") && (secondBody.name == "Slider")){
collisionBall(firstBody, Slider: secondBody)
}
else if ((firstBody.name == "Slider") && (secondBody.name == "Enemy")) {
collisionBall(secondBody, Slider: firstBody)
}
}
}
func collisionBall(Enemy : SKSpriteNode, Slider : SKSpriteNode){
Enemy.physicsBody?.dynamic = true
Enemy.physicsBody?.affectedByGravity = true
Enemy.physicsBody?.mass = 4.0
Slider.physicsBody?.mass = 4.0
Enemy.removeAllActions()
Enemy.physicsBody?.contactTestBitMask = 0
Enemy.physicsBody?.collisionBitMask = 0
Enemy.name = nil
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//Slider.hidden = false
FastButton.hidden = false
Title.hidden = true
Text.hidden = true
EnemyTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(GameScene.Enemies), userInfo: nil, repeats: true)
//Physics
Slider.physicsBody?.categoryBitMask = PhysicsCatagory.Slider
Slider.physicsBody?.collisionBitMask = PhysicsCatagory.Enemy
Slider.physicsBody?.contactTestBitMask = PhysicsCatagory.Enemy
Slider.name = "Slider"
Slider.physicsBody?.dynamic = true
Slider.physicsBody?.affectedByGravity = true
if gameStarted == false{
gameStarted = true
}
else if gameStarted == true{
}
}
func moveClockWise(){
let dx = Slider.position.x / 2
let dy = Slider.position.y / 2
let rad = atan2(dy, dx)
let Path = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 90, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.followPath(Path.CGPath, asOffset: false, orientToPath: true, speed: 150)
//let rotate = SKAction.rotateByAngle(75, duration: 100)
Slider.runAction(SKAction.repeatActionForever(follow).reversedAction())
//Slider.runAction(SKAction.repeatActionForever(rotate).reversedAction())
}
func Enemies(){
let Enemy = SKSpriteNode(imageNamed: "darkRedDot")
Enemy.size = CGSize(width: 20, height: 20)
//Physics
Enemy.physicsBody = SKPhysicsBody(circleOfRadius: Enemy.size.width / 2)
Enemy.physicsBody?.categoryBitMask = PhysicsCatagory.Enemy
Enemy.physicsBody?.contactTestBitMask = PhysicsCatagory.Slider //| PhysicsCatagory.Circle
Enemy.physicsBody?.collisionBitMask = PhysicsCatagory.Slider //| PhysicsCatagory.Circle
Enemy.physicsBody?.dynamic = true
Enemy.physicsBody?.affectedByGravity = false
Enemy.name = "Enemy"

The contact is not beginning because your slider has no physics body, making it impossible to recognize contact. Unless, it's not in this code, your slider has no physics body, but your enemy does. Try declaring Slider.phisicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "blocker1"), size: Slider.size)
Also, you should read about standard naming conventions in Swift. It is recommended that your variables always start with lowercase letters (eg. enemyTimer, circle, slider, fastButton, etc...).

Related

SKSpriteNode will not appear when its physics body is dynamic

The SKSprite will only appear on the scene if .isDynamic = false otherwise it will not show up. Although it does not show up, it seems to be contacting with the other nodes in the scene.
I have tried playing around with the x and y values of the "lemming" node but this still results in it not appearing.
The Question:
What do I need to add or remove in order to make the "lemming" node be affected
by gravity, appear in the scene, and contact correctly with the "cliff" node?
In the GameScene.swift file:
//MARK: - Nodes
var background = SKSpriteNode()
var cliff = SKTexture()
var lemming = SKSpriteNode()
//MARK: -Catergories
let lemmingCatergory : UInt32 = 0x1 << 1
let cliffCatergory : UInt32 = 0x1 << 2
//MARK: - Start of game functions
override func didMove(to view: SKView) {
backgroundColor = UIColor.green
scaleMode = .aspectFit
size = UIScreen.main.bounds.size
physicsWorld.gravity = CGVector(dx: 0, dy: -9.81)
physicsWorld.contactDelegate = self
addBackground()
addCliff()
addLemming()
view.showsPhysics = true
}
//MARK: -Node customization
private func addBackground() {
background = SKSpriteNode(imageNamed: "background")
background.name = "Background"
background.position = CGPoint(x: frame.midX, y: frame.midY)
background.size = UIScreen.main.bounds.size
background.physicsBody = SKPhysicsBody(rectangleOf: background.frame.size)
background.physicsBody?.isDynamic = false
background.physicsBody?.affectedByGravity = false
addChild(background)
}
private func addCliff() {
var cliffNode : SKSpriteNode!
cliff = SKTexture(imageNamed: "cliff")
cliffNode = SKSpriteNode(texture: cliff)
cliffNode.name = "Cliff"
cliffNode.position = CGPoint(x: frame.midX, y: frame.midY)
cliffNode.size = UIScreen.main.bounds.size
cliffNode.physicsBody = SKPhysicsBody(texture: cliff, size: cliff.size())
cliffNode.physicsBody!.categoryBitMask = cliffCatergory
cliffNode.physicsBody!.collisionBitMask = lemmingCatergory
cliffNode.physicsBody!.contactTestBitMask = lemmingCatergory
cliffNode.physicsBody?.isDynamic = true
cliffNode.physicsBody?.affectedByGravity = false
cliffNode.physicsBody?.pinned = true
cliffNode.physicsBody?.allowsRotation = false
addChild(cliffNode)
}
private func addLemming() {
lemming = SKSpriteNode(imageNamed: "lemming")
lemming.size = CGSize(width: lemming.size.width / 15, height: lemming.size.height / 15)
lemming.position = CGPoint(x: 0.5, y: 0)
lemming.physicsBody = SKPhysicsBody(rectangleOf: lemming.frame.size)
lemming.physicsBody!.categoryBitMask = lemmingCatergory
lemming.physicsBody!.collisionBitMask = cliffCatergory
lemming.physicsBody!.contactTestBitMask = cliffCatergory
lemming.physicsBody?.isDynamic = true
lemming.physicsBody?.affectedByGravity = true
addChild(lemming)
}
func didBegin(_ contact: SKPhysicsContact) {
print("Body A: \(String(describing: contact.bodyA.node!.name))")
print("Body B: \(String(describing: contact.bodyB.node!.name))")
}

SpriteKit - didBegin contact is called 30 times instead of 1 time

I am making a little FlappyBird clone and have got everything working as it should until I changed the physics body of the bird to be exact to the texture. Now what it does is when it flies through the gap in the pipes it counts 30 points instead of just 1 point.
This only happens when I use the texture exact physics body which I need because the bird isn't round nor rectangular.
How would I go about making the collision so it only collides once with each gap node. I have tried setting the categoryBitBask to 0 after the contact but then all the gaps after don't add to the point count anymore at all.
Here is the full game code:
var score = 0
class GameScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var bg = SKSpriteNode()
var ground = SKSpriteNode()
var scoreLabel = SKLabelNode(fontNamed: "Candice")
var gameOverLabel = SKLabelNode(fontNamed: "Candice")
var countbg = SKSpriteNode()
var timer = Timer()
enum ColliderType: UInt32 {
case Bird = 1
case Object = 2
case Gap = 4
}
var gameOver = false
let swooshSound = SKAction.playSoundFileNamed("sfx_swooshing.wav", waitForCompletion: false)
let pointSound = SKAction.playSoundFileNamed("sfx_point.wav", waitForCompletion: false)
let hitSound = SKAction.playSoundFileNamed("sfx_hit.wav", waitForCompletion: false)
#objc func makePipes() {
let movePipes = SKAction.move(by: CGVector(dx: -2 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width / 150))
let removePipes = SKAction.removeFromParent()
let moveAndRemovePipes = SKAction.sequence([movePipes, removePipes])
let gapHeight = bird.size.height * 2.8
let movementAmount = arc4random() % UInt32(self.frame.height) / 2
let pipeOffset = CGFloat(movementAmount) - self.frame.height / 4
let pipeTexture = SKTexture(imageNamed: "pipe1.png")
let pipe1 = SKSpriteNode(texture: pipeTexture)
pipe1.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeTexture.size().height / 2 + gapHeight / 2 + pipeOffset)
pipe1.zPosition = 2
pipe1.run(moveAndRemovePipes)
pipe1.physicsBody = SKPhysicsBody(rectangleOf: pipeTexture.size())
pipe1.physicsBody!.isDynamic = false
pipe1.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(pipe1)
let pipe2Texture = SKTexture(imageNamed: "pipe2.png")
let pipe2 = SKSpriteNode(texture: pipe2Texture)
pipe2.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY - pipeTexture.size().height / 2 - gapHeight / 2 + pipeOffset)
pipe2.zPosition = 2
pipe2.run(moveAndRemovePipes)
pipe2.physicsBody = SKPhysicsBody(rectangleOf: pipe2Texture.size())
pipe2.physicsBody!.isDynamic = false
pipe2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(pipe2)
let gap = SKNode()
gap.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeOffset)
gap.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 1, height: gapHeight))
gap.physicsBody!.isDynamic = false
gap.run(moveAndRemovePipes)
gap.physicsBody!.contactTestBitMask = ColliderType.Bird.rawValue
gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue
self.addChild(gap)
}
func didBegin(_ contact: SKPhysicsContact) {
if gameOver == false {
if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {
score += 1
scoreLabel.text = String(format: "%05d", score)
run(pointSound)
} else {
self.speed = 0
run(hitSound)
gameOver = true
timer.invalidate()
bird.removeFromParent()
let changeSceneAction = SKAction.run(changeScene)
self.run(changeSceneAction)
}
}
}
//MARK: Change to Game Over Scene
func changeScene(){
let sceneToMoveTo = GameOverScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
setupGame()
}
func setupGame() {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.makePipes), userInfo: nil, repeats: true)
let groundTexture = SKTexture(imageNamed: "ground.png")
let moveGroundAnimation = SKAction.move(by: CGVector(dx: -groundTexture.size().width, dy: 0), duration: 7)
let shiftGroundAnimation = SKAction.move(by: CGVector(dx: groundTexture.size().width, dy: 0), duration: 0)
let moveGroundForever = SKAction.repeatForever(SKAction.sequence([moveGroundAnimation, shiftGroundAnimation]))
var i: CGFloat = 0
while i < 3 {
ground = SKSpriteNode(texture: groundTexture)
ground.position = CGPoint(x: self.size.width * i, y: self.size.height / 7.65)
ground.zPosition = 3
ground.run(moveGroundForever)
self.addChild(ground)
i += 1
}
let bottom = SKNode()
bottom.position = CGPoint(x: self.frame.midX, y: self.size.height / 7)
bottom.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.width, height: 1))
bottom.physicsBody!.isDynamic = false
bottom.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bottom.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
bottom.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(bottom)
let bgTexture = SKTexture(imageNamed: "bg.png")
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
bg.size = self.frame.size
bg.zPosition = 1
self.addChild(bg)
let birdTexture = SKTexture(imageNamed: "flappy1.png")
let bird2Texture = SKTexture(imageNamed: "flappy2.png")
let bird3Texture = SKTexture(imageNamed: "flappy3.png")
let bird4Texture = SKTexture(imageNamed: "flappy4.png")
let bird5Texture = SKTexture(imageNamed: "flappy5.png")
let bird6Texture = SKTexture(imageNamed: "flappy6.png")
let animation = SKAction.animate(with: [birdTexture, bird2Texture, bird3Texture, bird4Texture, bird5Texture, bird6Texture], timePerFrame: 0.1)
let makeBirdFlap = SKAction.repeatForever(animation)
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
bird.setScale(1)
bird.zPosition = 6
bird.run(makeBirdFlap)
self.addChild(bird)
bird.physicsBody = SKPhysicsBody.init(circleOfRadius: birdTexture.size().height / 2)
//bird.physicsBody = SKPhysicsBody(texture: birdTexture, size: birdTexture.size())
bird.physicsBody!.isDynamic = false
bird.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bird.physicsBody!.categoryBitMask = ColliderType.Bird.rawValue
bird.physicsBody!.collisionBitMask = ColliderType.Bird.rawValue
let countbg = SKSpriteNode(imageNamed: "count_bg.png")
countbg.position = CGPoint(x: self.size.width / 4.8, y: self.size.height * 0.94)
countbg.setScale(0.8)
countbg.zPosition = 4
addChild(countbg)
scoreLabel.fontSize = 80
scoreLabel.text = String(format: "%05d", score)
scoreLabel.fontColor = SKColor(red: 218/255, green: 115/255, blue: 76/255, alpha: 1)
scoreLabel.position = CGPoint(x: self.size.width / 4, y: self.size.height * 0.94)
scoreLabel.zPosition = 5
addChild(scoreLabel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if gameOver == false {
bird.physicsBody!.isDynamic = true
bird.physicsBody!.velocity = CGVector(dx: 0, dy: 0)
bird.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 280))
//run(swooshSound)
} else {
gameOver = false
score = 0
self.speed = 1
self.removeAllChildren()
setupGame()
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
If you would be using RxSwift, you would be able to easily get rid of those extra events easily by using debounce() or throttle() or distinctUntilChanged(). If you are willing to try this approach, give RxSpriteKit framework a go. Otherwise, store a timestamp of the last contact and ignore the following contacts until some time period elapses.

How to shoot with two or more bullets in Swift

I am trying to find a way to shoot more than one bullets as power up increases. Also it's only going up straight on the first 4 power ups, but I would like it to have a little angle as it reaches 5 and up.
Can someone help me implement that with the following codes I currently have?
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var player: SKSpriteNode!
var scoreLabel: SKLabelNode!
var score: Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
var gameTimer: Timer!
var possibleAliens = ["alien", "alien2", "alien3"]
//bitmask for alien and torpedo's physics body
let alienCategory:UInt32 = 0x1 << 1
let photonTorpedoCategory:UInt32 = 0x1 << 0
//lives
var livesArray:[SKSpriteNode]!
//powerUp
var powerUp: Int = 1
//didMove
override func didMove(to view: SKView) {
addLives()
starField = SKEmitterNode(fileNamed: "Starfield")
starField.position = CGPoint(x: 0, y: 1472)
starField.advanceSimulationTime(10)
self.addChild(starField)
starField.zPosition = -1
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width / 3.6, y: player.size.height / 2 + 20)
self.addChild(player)
//physicsWorld
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
//score and scoreLabel
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 80, y: self.frame.size.height - 60)
scoreLabel.fontName = "AmericanTypewriter-Bold"
scoreLabel.fontSize = 28
scoreLabel.fontColor = UIColor.white
score = 0
self.addChild(scoreLabel)
//create a timeInterval that can be changed depending on the difficulty
var timeInterval = 0.6
if UserDefaults.standard.bool(forKey: "hard"){
timeInterval = 0.2
}
//gameTimer
gameTimer = Timer.scheduledTimer(timeInterval: timeInterval, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
//motion Manager initialization in didMove
motionManger.accelerometerUpdateInterval = 0.2
//creatingan acceleration data in didMove
motionManger.startAccelerometerUpdates(to: OperationQueue.current!) { (data: CMAccelerometerData?, error: Error?) in
if let accelerometerData = data{
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.25
}
}
}
//func addLives
func addLives() {
//initialize livesArray from GameScene
livesArray = [SKSpriteNode]()
for live in 1 ... 3 {
let liveNode = SKSpriteNode(imageNamed: "shuttle")
liveNode.name = "live\(live)"
liveNode.position = CGPoint(x: self.frame.size.width - CGFloat((4-live)) * liveNode.size.width, y: self.frame.size.height - 60)
self.addChild(liveNode)
livesArray.append(liveNode)
}
}
//func addAlien
func addAlien() {
//using GK, pick possibleAliens[arrays] randomly, and shuffle
possibleAliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleAliens) as! [String]
//bring the aliens to random position
let alien = SKSpriteNode(imageNamed: possibleAliens[0])
let randomAlienPosition = GKRandomDistribution(lowestValue: 0, highestValue: 414)
//make the position constant, use randomAlien and get next integer and use CGFloat then set alien position
let position = CGFloat(randomAlienPosition.nextInt())
alien.position = CGPoint(x: position, y: self.frame.size.height + alien.size.height)
//physicsBody of addAlien
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
//make aliens move
let animationDuration: TimeInterval = 6
//SKAction to alien will make alien move from top to bottom of the screen, then remove alien from screen and from parent so it doesnt consume too much memory
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -alien.size.height), duration: animationDuration))
//THIS ACTION WILL SEE IF IT REACHES THE FINAL DESTINATION BEFORE IT GETS ERASED AND TAKES A LIFE
//A RUN ACTION THAT WILL PLAY SOUND WHEN PLAYER LOSES SOUND.
actionArray.append(SKAction.run{
self.run(SKAction.playSoundFileNamed("loose.mp3", waitForCompletion: false))
if self.livesArray.count > 0 {
let liveNode = self.livesArray.first
liveNode!.removeFromParent()
self.livesArray.removeFirst()
if self.livesArray.count == 0{
let transition = SKTransition.flipHorizontal(withDuration: 0.5)
let gameOver = SKScene(fileNamed: "GameOverScene") as! GameOverScene
gameOver.score = self.score
self.view?.presentScene(gameOver, transition: transition)
}
}
})
actionArray.append(SKAction.removeFromParent())
//make a run function on the alien to pass allong actionArray
alien.run(SKAction.sequence(actionArray))
}
//fire fireTorpedo when tapped
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
fireTorpedo()
}
//func FireTorpedo or bullet
func fireTorpedo(){
//adds sound, image and position of the torpedo
self.run(SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false))
let torpedoNode = SKSpriteNode(imageNamed: "torpedo")
//powerUp switch
switch (powerUp)
{
case 1:
torpedoNode.position.x = player.position.x
torpedoNode.position.y = player.position.y
case 2:
torpedoNode.position.y = player.position.y
torpedoNode.position.x = player.position.x - 10
torpedoNode.position.x = player.position.x + 10
default:
print("out of torpedo ammo")
break
}
torpedoNode.position.y += 5
//add physicsBody for torpedo just like the aliens
torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
torpedoNode.physicsBody?.isDynamic = true
torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
torpedoNode.physicsBody?.contactTestBitMask = alienCategory
torpedoNode.physicsBody?.collisionBitMask = 0
torpedoNode.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(torpedoNode)
//add animation like in alien
let animationDuration: TimeInterval = 0.3
//make torpedo move up and disappear
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
//run the torpedo
torpedoNode.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact){
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
//check if two bodies touch
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask{
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
//to findout which body is the torpedo and which is alien
if (firstBody.categoryBitMask & photonTorpedoCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as? SKSpriteNode, alienNode: secondBody.node as? SKSpriteNode)
}
}
func torpedoDidCollideWithAlien (torpedoNode: SKSpriteNode?, alienNode: SKSpriteNode?){
if let explosion = SKEmitterNode(fileNamed: "Explosion"){
if let alien = alienNode{
explosion.position = alien.position
}
self.addChild(explosion)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
if let torpedo = torpedoNode{
torpedo.removeFromParent()
}
if let alien = alienNode{
alien.removeFromParent()
}
//see the explosion effect longer and not disappear immediately with run function with action and completion handler
self.run(SKAction.wait(forDuration: 2)){
explosion.removeFromParent()
}
//add and update score
score += 5
}
}
override func didSimulatePhysics() {
player.position.x += xAcceleration * 50
if player.position.x < -20 {
player.position = CGPoint(x: self.size.width + 20, y: player.position.y)
} else if player.position.x > self.size.width + 20 {
player.position = CGPoint(x: -20, y: player.position.y)
}
}
}

didBeginContact not working in Swift 2 + SpriteKit

I'm working on a game, and I'm using spritekit and Swift.
This is my code:
import SpriteKit
struct collision {
static let arrow:UInt32 = 0x1 << 1
static let runner:UInt32 = 0x1 << 2
static let target:UInt32 = 0x1 << 3
static let targetCenter:UInt32 = 0x1 << 4
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var person = SKSpriteNode()
var box = SKSpriteNode()
var screenSize:CGSize!
var gameScreenSize:CGSize!
var gameStarted:Bool = false
var moveAndRemove = SKAction()
var boxVelocity:NSTimeInterval = 5.5
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -1.0)
self.physicsWorld.contactDelegate = self
screenSize = self.frame.size
gameScreenSize = view.frame.size
createPerson()
}
func createPerson() -> Void {
person.texture = SKTexture(imageNamed:"person")
person.setScale(1.0)
person.size = CGSize(width: 80, height: 80)
person.position = CGPoint(x: screenSize.width / 2, y: 150)
person.physicsBody = SKPhysicsBody(rectangleOfSize: person.size)
person.physicsBody?.affectedByGravity = false
person.physicsBody?.dynamic = false
self.addChild(person)
}
func createTarget() -> Void {
box = SKSpriteNode()
box.size = CGSize(width: 70, height: 100)
box.setScale(1.0)
box.position = CGPoint(x: (screenSize.width / 3) * 2, y: screenSize.height + box.size.height)
box.texture = SKTexture(imageNamed: "box")
box.physicsBody? = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
box.physicsBody?.affectedByGravity = false
box.physicsBody?.dynamic = true
box.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(box)
let distance = CGFloat(self.frame.height - box.frame.height)
let moveTargets = SKAction.moveToY(-distance, duration: boxVelocity)
let removeTargets = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([moveTargets,removeTargets])
box.runAction(moveAndRemove)
}
func createBall() ->Void {
let ball = SKSpriteNode()
ball.size = CGSize(width: 20, height: 22)
ball.zPosition = 5
let moveToXY = CGPoint(x: self.size.width, y: self.size.height)
ball.texture = SKTexture(imageNamed: "ball")
ball.position = CGPointMake(person.position.x + ball.size.width, person.position.y + ball.size.height)
ball.physicsBody? = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.dynamic = true
ball.physicsBody?.usesPreciseCollisionDetection = true
let action = SKAction.moveTo(moveToXY, duration: 1.5)
let delay = SKAction.waitForDuration(1.5)
ball.runAction(SKAction.sequence([action,delay]))
self.addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if gameStarted == false {
gameStarted = true
let spawn = SKAction.runBlock { () in
self.createTarget()
}
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spanDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spanDelayForever)
} else {
createBall()
boxVelocity -= 0.1
}
}
func didBeginContact(contact: SKPhysicsContact) {
print("Detect")
}
func didEndContact(contact: SKPhysicsContact) {
print("end detect")
}
override func update(currentTime: CFTimeInterval) {
}
}
But when I run the game, the collision between objects does not. I'm trying to solve a while, but found nothing. Can someone help me?
Project files.
Try with these to modifications:
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody?.categoryBitMask = collision.target
box.physicsBody?.collisionBitMask = collision.arrow
box.physicsBody?.contactTestBitMask = collision.targetCenter
and
ball.physicsBody = SKPhysicsBody(rectangleOfSize: ball.size)
ball.physicsBody?.categoryBitMask = collision.arrow
ball.physicsBody?.collisionBitMask = collision.target
ball.physicsBody?.contactTestBitMask = collision.target
Note the absence of "?" while you init the physicsBody and the new contactTestBitMask

Stopping the countUp speed rate

I just started to learn Swift and Sprite Kit. I need assistance with the following code.
I have a countUp that when the ball is touched the countUp starts but my problem is that every time I tap the ball the rate of speed in the countUp increases. I want the CountUp to be stable. Any help will be appreciated.
class Game: SKScene {
var Ball = SKSpriteNode(imageNamed: "Red.png")
var QuitOption = SKLabelNode()
var ScoreLabel = SKLabelNode()
var timescore = Int()
var timesecond = Int()
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.blackColor() // background for the display
self.physicsWorld.gravity = CGVectorMake(0, -9.8)
let SceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
SceneBody.friction = 0
self.physicsBody = SceneBody
Ball.size = CGSize(width: 120, height: 120)
Ball.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height*0.7)
Ball.physicsBody = SKPhysicsBody(circleOfRadius: 60)
Ball.physicsBody?.affectedByGravity = true
Ball.physicsBody?.restitution = 0.5
Ball.physicsBody?.linearDamping = 0
Ball.name = "Ball"
self.addChild(Ball)
QuitOption.text = "Quit"
QuitOption.fontName = "Noteworthy-Light"
QuitOption.fontColor = SKColor.greenColor()
QuitOption.fontSize = 35
QuitOption.position = CGPoint(x: self.frame.size.width/2 - 160, y: self.frame.size.height*1 - 110)
QuitOption.name = "Quit"
addChild(QuitOption)
ScoreLabel = SKLabelNode(fontNamed: "Noteworthy-Light")
ScoreLabel.fontSize = 50 // The + will move it to the right side and - to the left side for more accuracy.
ScoreLabel.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/4 + 400) // position of ScoreLabelNode
ScoreLabel.name = "Score+"
ScoreLabel.hidden = false
self.addChild(ScoreLabel)
}
// Making the ball jump after user touches ball
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
var touch = touches.first as! UITouch
var location = touch.locationInNode(self)
var node = self.nodeAtPoint(location)
if (node.name == "Quit"){
let myScene = GameScene(size: self.size)
myScene.scaleMode = scaleMode
let reveal = SKTransition.fadeWithDuration(1)
self.view?.presentScene(myScene, transition: reveal)
}
if (node.name == "Ball"){
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
Ball.physicsBody?.allowsRotation = true
Ball.physicsBody?.velocity = CGVectorMake(0, 0)
Ball.physicsBody?.applyImpulse(CGVectorMake(0, 100))
}
}
var actionwait = SKAction.waitForDuration(0.5)
var actionrun = SKAction.runBlock({
self.timescore++
self.timesecond++
if self.timesecond == 60 {self.timesecond = 0}
self.ScoreLabel.text = " \(self.timescore/60):\(self.timesecond)"})
ScoreLabel.runAction(SKAction.repeatActionForever(SKAction.sequence([actionwait,actionrun])))
}
}
I guess that's happening because you are running same block over and over again after every touch. To ensure you run block only once, you can use some Bool indicator and after the first run, set its value appropriately, like this:
if(!self.locked){
self.locked = true // at start, this value should be false
var actionrun = SKAction.runBlock({
self.timescore++
self.timesecond++
if self.timesecond == 60 {self.timesecond = 0}
self.ScoreLabel.text = " \(self.timescore/60):\(self.timesecond)"})
ScoreLabel.runAction(SKAction.repeatActionForever(SKAction.sequence([actionwait,actionrun])))
}
}
Something as an addition to this topic and for future readers could be found here.