Touch Sprite, make it jump up then fall down again(repeat as many times as spritenode is tapped.) - swift

I created a project where I have a ball and when the view loads, it falls down, which is good. I'm trying to get the ball to jump back up and fall down again when the Spritenode is tapped.
--This Question was edited--
Originally, I was able to get it to work when sprite.userInteractionEnabled = false. I had to turn this statement true in order to get the score to change.
Now I can't get the balls to fall and be tapped to jump. When I turn ball.physicsBody?.dynamic = true, the balls will fall due to gravity. How do I tap the sprite itself and make it jump.
GameScene.swift (For those who want to try the code for themselves.)
import SpriteKit
class GameScene: SKScene {
var ball: Ball!
private var score = 0 {
didSet { scoreLabel.text = "\(score)" }
}
override func didMoveToView(view: SKView) {
let ball = Ball()
scoreLabel = SKLabelNode(fontNamed:"Geared-Slab")
scoreLabel.fontColor = UIColor.blackColor()
scoreLabel.position = CGPoint( x: self.frame.midX, y: 3 * self.frame.size.height / 4 )
scoreLabel.fontSize = 100.0
scoreLabel.zPosition = 100
scoreLabel.text = String(score)
self.addChild(scoreLabel)
ball.position = CGPoint(x:self.size.width / 2.0, y: 440)
addChild(ball)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 120)
ball.physicsBody?.dynamic = true
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.restitution = 3
ball.physicsBody?.friction = 0
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.usesPreciseCollisionDetection = true
}
class Ball: SKSpriteNode {
init() {
let texture = SKTexture(imageNamed: "Ball")
super.init(texture: texture, color: .clearColor(), size: texture.size())
userInteractionEnabled = true
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let scene = self.scene as! GameScene
scene.score += 1
}
Before, it was a SKNode being tapped using CGVectorMake(impulse, velocity) now, it's a SKSpriteNode, and I tried using SKAction, but it either does not work, or I'm putting it in the wrong place(touches begin).

I tested your code and it seems that using firstBall.userInteractionEnabled = true is the cause. Without it, it should work. I did some research (here for example), but can't figure out what's the reason of this behavior with userInteractionEnabled. Or for what reason do you use userInteractionEnabled?
Update due to update of question
First ball.physicsBody?.restitution = 3 defines the bounciness of the physics body. The default value is 0.2 and the property must be between 0.0 ans 1.0. So if you set it to 3.0 it will cause some unexpected effects. I just deleted it to use the default value of 0.2.
Second, to make the ball jump after tap and increase the score I put
physicsBody?.velocity = CGVectorMake(0, 600)
physicsBody?.applyImpulse(CGVectorMake(0, 1100))
in the touchesBegan method of the Ball class
Result

Related

Swift / SpriteKit Collision producing varying results

I have a small test project (my first using Swift / XCode) which is designed to move me away from HTML5 and Canvas for game production.
The code compiles and runs fine. I use my iPhone as the test device rather than the built in simulator.
The symptoms of the problem are
that the lasers being repeatedly fired from the player's ship appear to occasionally bend around the aliens
the names being pulled out from the nodes are being shown as their default names not the names I assigned to them at creation
In some cases the collision works fine and the alien explosion is generated and the alien sprite node is removed from the scene.
I have named the alien nodes "alien" and the laser nodes "laser".
Both have their contactTestBitMask set to the same value.
Here is my GameScene.swift code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var lastUpdateTime: TimeInterval = 0
var delta: TimeInterval = 0
var sp_player: SKSpriteNode!
var stars: SKSpriteNode!
var deeperstars: SKSpriteNode!
var laser: SKSpriteNode!
var alien: SKSpriteNode!
var explosionSplat1: SKSpriteNode!
var playerScore: UInt32!
struct PhysicsCategory {
static let base:UInt32 = 0x1 << 0
static let alien:UInt32 = 0x1 << 1
static let laser:UInt32 = 0x1 << 2
static let player:UInt32 = 0x1 << 3
}
override func didMove(to view: SKView) { // called when the scene is presented into view (happens only once)
playerScore = 0
physicsWorld.contactDelegate = self
physicsWorld.gravity = .zero
// BACKGROUND
backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 48/255, alpha: 1.0)
print("Background color is set")
// WRAP THE STARFIELDS
// Front most layer of stars
let starsTexture = SKTexture(imageNamed: "stars.png")
let bgAnimation = SKAction.move(by: CGVector(dx: 0, dy: -starsTexture.size().height), duration: 5)
let bgReset = SKAction.move(by: CGVector(dx: 0, dy: starsTexture.size().height), duration: 0)
let bgConstantMotion = SKAction.repeatForever(SKAction.sequence([bgAnimation,bgReset]))
// Back layer of slower stars
let deeperStarsTexture = SKTexture(imageNamed: "stars-deeper.png")
let deeperStarsbgAnimation = SKAction.move(by: CGVector(dx: 0, dy: -deeperStarsTexture.size().height), duration: 8)
let deeperStarsbgReset = SKAction.move(by: CGVector(dx: 0, dy: deeperStarsTexture.size().height), duration: 0)
let deeperStarsbgConstantMotion = SKAction.repeatForever(SKAction.sequence([deeperStarsbgAnimation,deeperStarsbgReset]))
var i: CGFloat = 0
while i < 3
{
stars = SKSpriteNode(texture: starsTexture)
stars.position = CGPoint(x: frame.midX, y: starsTexture.size().height * i)
stars.size.height = frame.height
stars.run(bgConstantMotion)
stars.zPosition = -1
addChild(stars)
deeperstars = SKSpriteNode(texture: deeperStarsTexture)
deeperstars.position = CGPoint(x: frame.midX, y: deeperStarsTexture.size().height * i)
deeperstars.size.height = frame.height
deeperstars.run(deeperStarsbgConstantMotion)
deeperstars.zPosition = -1
addChild(deeperstars)
i += 1
}
// PLAYER
let playerTexture1 = SKTexture(imageNamed: "player-1.png")
let playerTexture2 = SKTexture(imageNamed: "player-2.png")
let playerAnimation = SKAction.animate(with: [playerTexture1, playerTexture2], timePerFrame: 0.2)
let constantAnimation = SKAction.repeatForever(playerAnimation)
sp_player = SKSpriteNode(texture: playerTexture1)
sp_player.position = CGPoint(x: frame.midX, y: (sp_player.size.height * 2))
sp_player.physicsBody = SKPhysicsBody(rectangleOf: sp_player.size)
sp_player.physicsBody!.isDynamic = false
sp_player.name = "player"
sp_player.run(constantAnimation)
addChild(sp_player)
// PLACE ALIENS
let alienTexture1 = SKTexture(imageNamed: "alien-1a.png")
let alienTexture2 = SKTexture(imageNamed: "alien-1b.png")
let alienAnimation = SKAction.animate(with: [alienTexture1, alienTexture2], timePerFrame: 0.4)
let constantAlienAnimation = SKAction.repeatForever(alienAnimation)
var x: CGFloat = 0, y: CGFloat = 0
while y < 6
{
while x < 6
{
alien = SKSpriteNode(texture: alienTexture1)
alien.position = CGPoint(x: 32 + (x * alien.size.width), y: (frame.size.height - (alien.size.height * 1.5) - (alien.size.height * y)))
print("Setting y to \(frame.size.height - (alien.size.height * y))")
alien.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: alien.size.width, height: alien.size.height))
alien.physicsBody!.isDynamic = false
alien.name = "alien"
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
alien.run(constantAlienAnimation)
addChild(alien)
x += 1
}
y += 1
x = 0
}
print("Sprites added to scene")
spawnLasers()
}
func spawnLasers()
{
let delay1 = SKAction.wait(forDuration: 0.5)
let spawn = SKAction.run {
let laserTexture = SKTexture(imageNamed: "laser-1.png")
self.laser = SKSpriteNode(texture: laserTexture)
self.laser.position = CGPoint(x: self.sp_player.position.x, y: self.sp_player.position.y + self.sp_player.size.height)
self.laser.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.laser.size.width, height: self.laser.size.height))
self.laser.physicsBody!.isDynamic = true
self.laser.physicsBody!.linearDamping = 0
self.laser.physicsBody!.allowsRotation = false
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.laser
self.laser.name = "laser"
self.addChild(self.laser)
let shoot = SKAction.moveTo(y: self.frame.size.height, duration: 1)
let killLaser = SKAction.removeFromParent()
let handleLaser = SKAction.sequence([shoot,killLaser])
self.laser.run(handleLaser)
}
let action = SKAction.sequence([delay1,spawn])
let constantLasers = SKAction.repeatForever(action)
self.run(constantLasers)
}
func didBegin(_ contact: SKPhysicsContact) {
var check: UInt32 = 0
if contact.bodyA.node != nil
{
check += 1
}
if contact.bodyB.node != nil
{
check += 1
}
if check == 2
{
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
{
// EXPLOSION
let explosionSplatTexture1 = SKTexture(imageNamed: "explosion-1a.png")
let explosionSplatTexture2 = SKTexture(imageNamed: "explosion-1b.png")
let explosionSplatTexture3 = SKTexture(imageNamed: "explosion-1c.png")
let explosionSplatTexture4 = SKTexture(imageNamed: "explosion-1d.png")
let explosionSplatAnimation = SKAction.animate(with: [explosionSplatTexture1, explosionSplatTexture2, explosionSplatTexture3, explosionSplatTexture4], timePerFrame: 0.1)
let killExplosion = SKAction.removeFromParent()
let explosionSequence = SKAction.sequence([explosionSplatAnimation,killExplosion])
explosionSplat1 = SKSpriteNode(texture: explosionSplatTexture1)
explosionSplat1.name = "explosion"
explosionSplat1.position = CGPoint(x: contact.bodyA.node!.position.x, y: contact.bodyA.node!.position.y)
addChild(explosionSplat1)
explosionSplat1.run(explosionSequence)
self.playerScore += 1
print("Score: \(self.playerScore!)")
contact.bodyA.node?.removeFromParent()
print("Alien named \(contact.bodyA.node?.name ?? "defaultAlienName") from scene")
contact.bodyB.node?.removeFromParent()
print("Laser named \(contact.bodyB.node?.name ?? "defaultLaserName") from scene")
}
}
}
func didEnd(_ contact: SKPhysicsContact) {
//print("Contact ended between \(contact.bodyA) and \(contact.bodyB)")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?)
{
// if let touch = touches.first {
// let position = touch.location(in: view)
// storedTouch = position
// }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
{
if let touch = touches.first {
let position = touch.location(in: view)
var playerpos: CGPoint!
playerpos = sp_player.position
let pl_move = SKAction.move(to: CGPoint(x: position.x, y: playerpos.y), duration: 0.1)
sp_player.run(pl_move)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?)
{
/*
Need to figure out how to use storedTouch properly
to move player relative to the screen touch co-ordinates
*/
// if let touch = touches.first {
// let position = touch.location(in: view)
// }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
if (lastUpdateTime > 0)
{
delta = currentTime - lastUpdateTime
} else {
delta = 0
}
lastUpdateTime = currentTime
}
}
When the game runs it produces this screen:
You can see the unexpected laser behavior here:
In my diags I get the following output from the collision function:
Score: 1
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 2
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 3
Alien named defaultAlienName removed from scene
Laser named laser removed from scene
Score: 4
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
Score: 5
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
Score: 6
Alien named defaultAlienName removed from scene
Laser named defaultLaserName removed from scene
This is most likely my complete lack of understanding for optionals and how collision actually works. I'd be super grateful for any insights.
In your alien loop, as well as spawnLasers(), you are not giving the sprite nodes an actual PhysicsBody category. For sprites to be able to detect contact between one another, they need a category name and a contact name.
So in your while loop (building the aliens), you need to have this:
alien.physicsBody!.categoryBitMask = PhysicsCategory.alien
alien.physicsBody!.contactTestBitMask = PhysicsCategory.laser
And in spawnLasers(), you want this added:
self.laser.physicsBody!.categoryBitMask = PhysicsCategory.laser
and change the contactTestBitMask to alien, not laser:
self.laser.physicsBody!.contactTestBitMask = PhysicsCategory.alien
Hopefully you can see that the alien category wants to know when lasers touch, and the laser category wants to know when the aliens touch.
To visually help you, turn on the show physics option, this way you can see the actual physics bodies you are dealing with.
To do this, in your GameViewController (or similar), find:
showsFPS = true
showsNodeCount = true
You want to add the following:
showsPhysics = true
This will help with seeing the actual physics bodies on screen.
In:
func didBegin(_ contact: SKPhysicsContact)
you are only testing for BodyA being alien and BodyB being laser.
This is the contact test:
if contact.bodyA.node!.name == "alien" && contact.bodyB.node!.name == "laser"
I believe BodyA could be laser and BodyB be alien. Basically the physics engine contact events could be "A hitting B", or "B hitting A".
Therefore, a quick and dirty solution is to create another if statement below the current one, but changing the body names, so:
if contact.bodyA.node!.name == "laser" && contact.bodyB.node!.name == "alien" {
and duplicate the code from your existing if statement, and changing the two print statements.
This isn't the ideal way to do it, but hopefully when you tidy it up you'll get an understanding of what the physics contact is doing.
I am hoping once you have implmented the above, you will be in a much better shape.

how to carry across values across scenes

I am trying to make a 'money' value show on 3 different scenes, and it is showing on the GameScene but not on the GameOverScene or the ShopScene.
this is the relevant code of the GameScene that is working:
func adjustMoney(by points: Int){
var money = UserDefaults().integer(forKey: "moneyscore")
money += points
MoneyLabel.text = "Money = " + UserDefaults().integer(forKey: "moneyscore").description
UserDefaults().set(money, forKey: "moneyscore")
}
func projectileDidCollideWithMonster(projectile: SKSpriteNode, monster: SKSpriteNode) {
print("Hit")
projectile.removeFromParent()
monster.removeFromParent()
monstersDestroyed += 1
adjustScore(by: 1)
adjustMoney(by: 2)
and this is the total code in the other scenes (there is just the ShopScene as it is the same in the other):
import Foundation
import SpriteKit
var welcomeLabel: SKLabelNode!
class ShopScene: SKScene {
var background = SKSpriteNode(imageNamed: "background")
override func didMove(to view: SKView) {
background.zPosition = -1
background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
addChild(background) welcomeLabel = SKLabelNode(fontNamed: "Chalkduster")
welcomeLabel.text = "Welcome to the shop!"
welcomeLabel.fontSize = 40
welcomeLabel.fontColor = SKColor.black
welcomeLabel.position = CGPoint(x: size.width/2, y: size.height/1.2)
addChild(welcomeLabel)
MoneyLabel = SKLabelNode(fontNamed: "Chalkduster")
MoneyLabel.text = "Money = \(money)"
MoneyLabel.fontSize = 20
MoneyLabel.fontColor = SKColor.black
MoneyLabel.position = CGPoint(x: size.width/6.2, y: size.height/1.35)
addChild(MoneyLabel)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let gameScene = GameScene(size: self.size)
gameScene.scaleMode = .aspectFill
self.view?.presentScene(gameScene, transition: SKTransition.doorsCloseHorizontal(withDuration: 1.0))
}
}
However it only says money = 0
---- I'm not sure if it is of any relevance but before my 'projectile' and 'monster' first collide each game all my values of high score and score and money are 0 then they go back to their saved values, apart from score obviously that goes to 0. Score is the only value that isn't saved and that does move across to the GameOverScene.
You need to access UserDefaults().integer(forKey: "moneyscore") to use this in both the GameOverScene or the ShopScene class as you are updating userdefaults value(money).
This is actually for your specific case only. On the other hand, if you are interested to pass data in different classes(you can mention them as screens if they have views), you can follow different methodologies. You can follow this link for references.

how to keep the energy in my spritekit node constant while keeping gravity

I have a project where a ball rolls in between 2 hills and I want it to keep rolling forever. The hills are made by using spline points( There are 3 points, at (0,300), (375,0), and (750,300)). I have made sure that the ball and the ground has friction and restitution set to 0 but it still rolls back and forth, going up the hill less than it did the previous time.
Here is what the hills and ball look like:
Image
Edit: I was asked to add some code so here is my GameScene.swift file:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var ball = SKShapeNode()
func createView(){
removeAllActions()
removeAllChildren()
ball = SKShapeNode(circleOfRadius: 20)
ball.position = CGPoint(x:0, y:350)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody?.restitution = 0
ball.physicsBody?.friction = 0
ball.physicsBody?.mass = 0.1
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.isDynamic = true
ball.fillColor = .red
addChild(ball)
ball.physicsBody?.velocity = CGVector(dx: 0, dy: -400)
ball.physicsBody?.allowsRotation = true
self.physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
var splinepoints = [CGPoint(x: 0 , y: 0),
CGPoint(x: 0, y: 300),
CGPoint(x: 375, y: 0),
CGPoint(x: 750, y: 300)]
let ground = SKShapeNode(splinePoints: &splinepoints, count: splinepoints.count)
ground.physicsBody = SKPhysicsBody(edgeChainFrom: ground.path!)
ground.physicsBody?.restitution = 0.0
ground.physicsBody?.friction = 0.0
addChild(ground)
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 0
self.physicsBody = border
}
override func didMove(to view: SKView) {
createView()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 0
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(2), execute: {
})
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(5), execute: {
self.createView()
})
}
}
The physics in SpriteKit are a basic representation of real life. A lot of the principles taught in real-life physics courses are the same in SpriteKit.
Your ball is expending all its kinetic energy to roll up one hill. Rolling back down the hill doesn't give it enough energy to roll back up the other side. Eventually, the ball will stop rolling altogether and sit at the lowest point between the two hills.
Your code would have to add enough to the CGVector of your ball when it touches a node at the top of one side to get it to roll back down and up the other side to touch another node to add to the CGVector of the ball to get it to roll back down and up the first hill to touch the first node and add to the CGVector of the ball, etc. This should create an infinite rolling ball that never runs out of energy.

Label not showing up (Swift 4 SpriteKit)

I am making a game in Xcode with Swift 4, SpriteKit. My crash detection is working, however when I try to make text appear onto the screen when the player crashes, the text doesn't appear. My game basically has the use control a rocket ship which must dodge meteors, the player also has the ability to fire bullets. I have detected when the rocket ship hits a meteor but cannot get text to appear onto the screen. I know for a fact that the crash detection is working, because whenever I add a print statement in the didBegin function, the print statement executes. So it must be that way im displaying the label, but im not sure what I'm doing wrong with the label.
Here's my code:
import SpriteKit
import GameplayKit
import UIKit
class GameScene: SKScene, SKPhysicsContactDelegate{
let player = SKSpriteNode(imageNamed: "spaceship")
let stars = SKSpriteNode(imageNamed: "stars")
let meteor = SKSpriteNode(imageNamed: "meteor")
override func didMove(to view: SKView) {
physicsWorld.contactDelegate = self
print(frame.size.width)
print(frame.size.height)
stars.position = CGPoint(x:0, y:0)
stars.zPosition = 1
player.position = CGPoint(x:0, y:-320)
player.zPosition = 4
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.affectedByGravity = false
player.physicsBody?.isDynamic = false
player.physicsBody?.categoryBitMask = 2
player.physicsBody?.collisionBitMask = 1
player.physicsBody?.contactTestBitMask = 1
self.addChild(player)
self.addChild(stars)
self.addMeteor()
}
func didBegin(_ contact: SKPhysicsContact) {
gameOver()
}
func addMeteor() {
meteor.physicsBody = SKPhysicsBody(circleOfRadius: meteor.size.width / 2)
meteor.physicsBody?.affectedByGravity = false
meteor.setScale(0.50)
meteor.position = CGPoint(x:Int(arc4random()%300),y:Int(arc4random()%600))
//meteor.position = CGPoint(x:0 , y:0)
meteor.zPosition = 4
meteor.physicsBody?.categoryBitMask = 1
meteor.physicsBody?.collisionBitMask = 0
meteor.physicsBody?.contactTestBitMask = 2
self.addChild(meteor)
}
func fireBullet() {
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.position = player.position
bullet.setScale(0.5)
bullet.zPosition = 3
self.addChild(bullet)
let moveBullet = SKAction.moveTo(y: self.size.height + bullet.size.height, duration: 1)
let deleteBullet = SKAction.removeFromParent()
let bulletSequence = SKAction.sequence([moveBullet, deleteBullet])
bullet.run(bulletSequence)
}
func gameOver() {
print("Game Over!")
var gameOverLabel: SKLabelNode!
gameOverLabel = SKLabelNode(fontNamed: "Chalkduster")
gameOverLabel.text = "Game Over! You lost!"
gameOverLabel.horizontalAlignmentMode = .right
gameOverLabel.position = CGPoint(x: 0, y:0)
self.addChild(gameOverLabel)
print("Label added")
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
fireBullet()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch: AnyObject in touches {
let pointOfTouch = touch.location(in: self)
let previousPointOfTouch = touch.previousLocation(in: self)
let amountDragged = pointOfTouch.x - previousPointOfTouch.x
player.position.x += amountDragged
}
}
override func update(_ currentTime: TimeInterval) {
meteor.position.y -= 6
if meteor.position.y < player.position.y - 300{
meteor.removeFromParent()
addMeteor()
}
}
}
Not sure what I'm doing wrong? I call the gameOver() function in didBegin which is supposed to be executed every time collision is detected. But nothing happens? The meteor simply goes past the rocket ship. If someone could help me that'd be awesome!
As i see you are trying to center the label on the screen, you may try to add this to your code, on gameover func:
1.Try change the position to this
gameOverLabel.position = CGPoint(x: (self.scene!.frame.width / 2) - (self.scene!.frame.width * self.scene!.anchorPoint.x) / 2, y:(self.scene!.frame.height / 2) - (self.scene!.frame.height * self.scene!.anchorPoint.y))
2.I don't know if you have something that is over the label and affecting its view, so try to:
gameOverLabel.zPosition = 10 //A number higher than any other zPosition
3.The label may be too small...try:
gameOverLabel.fontSize = 30 //Adjust it to what you want
4.Just a tip:
move the:
var gameOverLabel: SKLabelNode!
to next line after the
let meteor = SKSpriteNode(imageNamed: "meteor")
This way you will have a universal label, and do not have to regenerate the label every time.
If you do this, just a final tip...I don't know if you have a func like gameRestart() or something like this...but make sure that when you restart the game, you remove the label:
gameOverLabel.removeFromParent()
If you don't do this, when the app try to add the label again it will crash!
Hope it helps!
You are not moving any of your objects as far as your physics in concerned. You need to apply forces instead of manually moving a position. Also your player dynamic is set to false, which means that he never moves, so contacts only happen on the meteor end.

SKPhysicsContactDelegate collision detection using Swift

I am trying to fix an issue I am having with the SKPhysicsContactDelegate collision detection. I have two nodes, nodeA and nodeB, nodeA is stationary on the screen while nodeB is able to be dragged around the screen by the users finger. nodeA needs to be able to detect if nodeB is overlapping it. The didBeginContact and didEndContact methods are being called multiple times which through research I have found to be an expected behavior. To get around this issue I simply set an integer variable to 0 and increment it each time there was a contact and decrement it each time a contact ended. If the value is greater than 0 then the two nodes are overlapping and if the value is equal to 0 then they are not. This works fine until the user drags nodeB over nodeA too fast. When this happens the contact methods are not always called the correct amount of times. For example, there may be 3 contacts detected but only two end contacts (or even none), which makes the program think that the two nodes are still overlapping even when they are not. I am assuming that this is happening because the user is dragging the node faster than the program can update. Is there anything I can do to get around this? Basically I just need to know exactly when the two nodes are overlapped and when they are not. Also note that the nodes are convex shapes. Below are my contact methods:
func didBeginContact(contact: SKPhysicsContact)
{
let contactMask = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
if contactMask == 3
{
startContact++
timerStart = true
}
}
func didEndContact(contact: SKPhysicsContact)
{
let contactMask = contact.bodyA.categoryBitMask + contact.bodyB.categoryBitMask
if contactMask == 3
{
startContact--
if startContact == 0
{
timerStart = false
}
}
}
You can check if one node intersect the other by using intersectsNode: method. From the docs about this method:
Returns a Boolean value that indicates whether this node intersects
the specified node.
Also important part to keep in mind is:
The two nodes are considered to intersect if their frames intersect.
The children of both nodes are ignored in this test.
import SpriteKit
class GameScene: SKScene {
var stationaryNode :SKSpriteNode = SKSpriteNode(color: SKColor.grayColor(), size: CGSize(width: 100, height: 100))
var moveableNode :SKSpriteNode = SKSpriteNode(color: SKColor.purpleColor(), size: CGSize(width: 100, height: 100))
let debugLabel :SKLabelNode = SKLabelNode(fontNamed: "ArialMT")
override func didMoveToView(view: SKView) {
setupScene()
}
func setupScene(){
stationaryNode.name = "stationaryNode"
stationaryNode.zRotation = 0.2
stationaryNode.zPosition = 1
stationaryNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame))
addChild(stationaryNode)
moveableNode.name = "moveableNode"
moveableNode.zRotation = 0.4
moveableNode.zPosition = 2
moveableNode.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)-200)
addChild(moveableNode)
debugLabel.fontSize = 18
debugLabel.fontColor = SKColor.yellowColor()
debugLabel.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)+200)
addChild(debugLabel)
updateDebugLabel()
}
func updateDebugLabel(){
let intersectionDetected:String = stationaryNode.intersectsNode(moveableNode) ? "YES" : "NO"
debugLabel.text = "Overlapping : " + intersectionDetected
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let previousPosition = touch.previousLocationInNode(self)
let node: SKNode? = nodeAtPoint(location)
if let nodeName = node?.name{
if nodeName == "moveableNode" {
let translation = CGPoint(x: location.x - previousPosition.x , y: location.y - previousPosition.y )
node!.position = CGPoint(x: node!.position.x + translation.x, y: node!.position.y + translation.y)
}
}
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
updateDebugLabel()
}
}
I guess that this solution works a bit better than when using physics engine for detecting contacts for such a fast moving objects. Still, moving the object extremely fast might have an unpredictable result.