Detecting collision on SpriteKit from two objects - swift

I am having some trouble detecting the collision of two objects when i shoot from my cannon. The goal is to shoot a bullet from it and remove as soon as it colides with the obstacle. It seems there is no collision going on at all in the game, since i cannot receive the "Collision" message i set. Take a look at the following code:
class GameScene: SKScene, SKPhysicsContactDelegate {
enum BodyType: UInt32 {
case bullet = 1
case line = 2
case breaker = 4
}
i've enumerated the components so i can set them in the category Bitmask. After that, i did a setup from shooting when a touch begins.
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let bullet = SKSpriteNode(imageNamed: "bullet.png")
bullet.position = self.shooterTriangle.position
var actionRotate = SKAction.rotateToAngle(CGFloat(2 * -M_PI), duration: NSTimeInterval(0.1))
let actionMove = SKAction.moveTo(CGPoint(x: 500, y: size.height * 0.5), duration: NSTimeInterval(0.5))
let removeBullet = SKAction.removeFromParent()
bullet.runAction(SKAction.sequence([actionRotate]))
bullet.runAction(SKAction.sequence([actionMove, removeBullet]))
addChild(bullet)
// Bullet Physics Start ****
bullet.physicsBody? = SKPhysicsBody(circleOfRadius: bullet.size.width/2.0)
bullet.physicsBody?.dynamic = true
bullet.physicsBody?.affectedByGravity = false
bullet.physicsBody?.allowsRotation = false
bullet.physicsBody?.categoryBitMask = BodyType.bullet.rawValue
bullet.physicsBody?.contactTestBitMask = BodyType.line.rawValue
bullet.physicsBody?.collisionBitMask = BodyType.line.rawValue
}
}
Then i`ve add the line i want the bullet to colide with in a loadGameComponents function...
func loadGameComponents() {
//Load line....
let line = SKSpriteNode(imageNamed: "Line_1.png")
line.position = CGPoint(x: size.width * 0.95, y: size.height * (2.0))
addChild(line)
var actualSpeedDuration = 2.2
let lineActionMove = SKAction.moveTo(CGPoint(x: size.width * 0.95, y: size.height * 0.6), duration: NSTimeInterval(actualSpeedDuration))
line.runAction(SKAction.sequence([lineActionMove]))
// Line Physics starts! *****
line.physicsBody? = SKPhysicsBody(rectangleOfSize: CGSize(width: line.size.width, height: line.size.height))
line.physicsBody?.dynamic = false
line.physicsBody?.affectedByGravity = false
line.physicsBody?.allowsRotation = false
line.physicsBody?.categoryBitMask = BodyType.line.rawValue
line.physicsBody?.contactTestBitMask = BodyType.bullet.rawValue
line.physicsBody?.collisionBitMask = 0
Finally, i've defined the contactDelegate = self in my didLoad function and included the didBeginContact method
func didBeginContact(contact: SKPhysicsContact) {
println("Contact!!!")
}
I am not receiving the message at all, but i am not sure what is going wrong!
Thanks for the patience! :)

Looks like the main problem is that i used to place the optional indicator (?) when declaring the physics body of an sprite. correction should be made in this lines...
line.physicsBody? = SKPhysicsBody(rectangleOfSize: CGSize(width: line.size.width, height: line.size.height))
bullet.physicsBody? = SKPhysicsBody(circleOfRadius: bullet.size.width/2.0)
Removing them solved my problem :)
I also included the view.showPhysics = true in my didMoveToView method so it is easy to see the boundaries between the sprites.

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 detect collision without physics

I want to detect when two bodies contact without using didBeginContact() function
I try with CGRectIntersectsRect() without any effect.
class GameScene: SKScene, SKPhysicsContactDelegate {
// Player
var _player = SKNode()
var platform = SKNode()
let heroCategory: UInt32 = 1 << 0
let platformCategory: UInt32 = 1 << 1
override func didMoveToView(view: SKView) {
/* Setup your scene here */
// Setup
self.anchorPoint = CGPointMake(0.5, 0.5)
self.physicsWorld.gravity = CGVectorMake(0.0, -2.0);
self.physicsWorld.contactDelegate = self
// Start populating
_player = creatPlayer()
self.addChild(_player)
platform = creatPlatform(1, atPosition: CGPoint(x: 0, y: -200))
self.addChild(platform)
let platformTwo = creatPlatform(2, atPosition: CGPoint(x: 0, y: 200))
self.addChild(platformTwo)
}
func creatPlatform(type: Int, atPosition: CGPoint) -> PlatformNode {
let node = PlatformNode()
node.platformType = type
node.position = atPosition
let sprite = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 200, height: 50))
node.addChild(sprite)
node.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
node.physicsBody?.dynamic = false;
node.physicsBody?.categoryBitMask = platformCategory
node.physicsBody?.contactTestBitMask = heroCategory
node.physicsBody?.collisionBitMask = 0;
return node
}
func creatPlayer() -> SKNode {
let playerNode = SKNode()
let sprite = SKSpriteNode(color: UIColor.whiteColor(), size: CGSize(width: 50, height: 50))
playerNode.addChild(sprite)
playerNode.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
playerNode.physicsBody?.dynamic = false
playerNode.physicsBody?.allowsRotation = false
playerNode.physicsBody?.restitution = 1.0
playerNode.physicsBody?.friction = 0.0
playerNode.physicsBody?.angularDamping = 0.0
playerNode.physicsBody?.linearDamping = 0.0
playerNode.physicsBody?.categoryBitMask = heroCategory
playerNode.physicsBody?.contactTestBitMask = platformCategory
playerNode.physicsBody?.collisionBitMask = 0;
return playerNode
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
_player.physicsBody?.dynamic = true
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if(CGRectIntersectsRect(platform.frame, _player.frame)){
println("Collision")
}
}
}
Maybe using CGRectIntersectsRect is a wrong approach, but I can be used instead?
The frame property
The frame property is a rectangle in the parent’s coordinate system
that contains the node’s content, ignoring the node’s children.
Also from docs:
The frame is non-empty if the node’s class draws content.
And because SKNode class does not perform any drawing of its own, and your platform is probably subclass of SKNode and player is SKNode the frame property is always (0,0,0,0).
You can access the real size of a frame in few ways:
You can make player and platform as subclasses of SKSpriteNode.
You can leave them as they are and access the child SKSpriteNode inside them.
You can leave as they are and use calcualteAccumulatedFrame() method.
About calcualteAccumulatedFrame() from docs:
A node’s accumulated frame, retrieved by calling the
calculateAccumulatedFrame method, is the largest rectangle that
includes the frame of the node and the frames of all its descendants.

How to make joints not rotate using sprite kit or another way of making dynamic 2d water

I want to creat a dynamic 2D water, i', following this unity tutorials
http://gamedevelopment.tutsplus.com/tutorials/creating-dynamic-2d-water-effects-in-unity--gamedev-14143
http://gamedevelopment.tutsplus.com/tutorials/make-a-splash-with-2d-water-effects--gamedev-236
And also this one
http://blog.prime31.com/water2d-part1/
Here is my code
class GameScene: SKScene, SKPhysicsContactDelegate {
var box: SKSpriteNode!
var nodes:[SKNode] = []
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.anchorPoint = CGPointMake(0.5, 0.5)
// Set physics body
let borderBody: SKPhysicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame);
borderBody.friction = 0.0;
self.physicsBody = borderBody;
// Set contact delegate
self.physicsWorld.contactDelegate = self;
box = SKSpriteNode(color: UIColor.blackColor(), size: CGSize(width: 200, height: 20))
box.position = CGPointMake(0, 0)
box.physicsBody = SKPhysicsBody(rectangleOfSize: box.size)
box.physicsBody!.dynamic = false
self.addChild(box)
let one = SKSpriteNode(color: UIColor.greenColor(), size: CGSize(width: 20, height: 20))
one.position = CGPointMake(box.position.x - box.frame.size.width/2, box.position.y + box.frame.size.height * 2)
one.physicsBody = SKPhysicsBody(rectangleOfSize: one.size)
one.physicsBody!.allowsRotation = false
self.addChild(one)
nodes.append(one)
self.attachPoint(box, point2: one, box: true)
let two = SKSpriteNode(color: UIColor.greenColor(), size: CGSize(width: 20, height: 20))
two.position = CGPointMake(box.position.x, box.position.y + box.frame.size.height * 2)
two.physicsBody = SKPhysicsBody(rectangleOfSize: two.size)
two.physicsBody!.allowsRotation = false
self.addChild(two)
nodes.append(two)
self.attachPoint(box, point2: two, box: true)
let three = SKSpriteNode(color: UIColor.greenColor(), size: CGSize(width: 20, height: 20))
three.position = CGPointMake(box.position.x + box.frame.size.width/2, box.position.y + box.frame.size.height * 2)
three.physicsBody = SKPhysicsBody(rectangleOfSize: three.size)
three.physicsBody!.allowsRotation = false
self.addChild(three)
nodes.append(three)
self.attachPoint(box, point2: three, box: true)
self.attachPoint(one, point2: two, box: false)
self.attachPoint(two, point2: three, box: false)
}
func attachPoint(point1: SKSpriteNode, point2: SKSpriteNode, box: Bool){
if(box == true){
let newPoint1 = CGPointMake(self.frame.size.width/2 + point2.position.x, self.frame.size.height/2 + point1.position.y)
let newPoint2 = CGPointMake(self.frame.size.width/2 + point2.position.x, self.frame.size.height/2 + point2.position.y)
// create a joint between two bodies
let joint: SKPhysicsJointSpring = SKPhysicsJointSpring.jointWithBodyA(point1.physicsBody, bodyB: point2.physicsBody, anchorA: newPoint1, anchorB: newPoint2)
joint.damping = 2.0
joint.frequency = 9.0;
self.physicsWorld.addJoint(joint)
} else {
let newPoint1 = CGPointMake(self.frame.size.width/2 + point1.position.x, self.frame.size.height/2 + point1.position.y)
let newPoint2 = CGPointMake(self.frame.size.width/2 + point2.position.x, self.frame.size.height/2 + point2.position.y)
// create a joint between two bodies
let joint: SKPhysicsJointSpring = SKPhysicsJointSpring.jointWithBodyA(point1.physicsBody, bodyB: point2.physicsBody, anchorA: newPoint1, anchorB: newPoint2)
joint.damping = 2.0
joint.frequency = 9.0;
self.physicsWorld.addJoint(joint)
}
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
let node = nodes[2]
node.physicsBody?.velocity.dy = 20000
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Here is the result
This result without changes
After touch, execute code in touchesBegan method
After step 2, the nodes start to rotate and fall bellow the node
If they don't rate, following the the tutorials I will have to make SKShapeNode with the color of the node. But SKShapeNode is expensive, is there another way this effect can be accomplished?
The easiest way is to use image for the top part of the water and make it move back and forth, but it won't be dynamic..
If you have ever play Tiny Wings, the water there is implemented exactly the same way in the tutorials.
I don't know, maybe this can't be made in SpriteKit or I just don't know how

Up and down movement in sprite kit and swift?

Right now, I am creating a new game that will involve a ball that moves up and down the screen by itself without touching a button or the screen. I have been looking everywhere for a solution, but I haven't found one. I know how to make the ball move up and down in objective-c, but not in swift or spritekit. I don't know if the following code will help or not. Any solution is accepted.
class GamePlayScene: SKScene, SKPhysicsContactDelegate {
var ball = SKSpriteNode(imageNamed: "green ball.png")
override func didMoveToView(view: SKView) {
//setup scene
physicsWorld.gravity = CGVector.zeroVector
self.scene?.backgroundColor = UIColor.blackColor()
self.scene?.size = CGSize(width: 640, height: 1136)
ball.position = CGPoint (x: self.size.width * 0.5, y: self.size.width * 0.9)
ball.size = CGSize (width: 80, height: 82)
let moveBallUp = SKAction.moveToY(600, duration: 0.4)
let moveBallDown = SKAction.moveToY(293, duration: 0.4)
self.addChild(ball)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
let moveBallUp = SKAction.moveToY(600, duration: 0.4)
let moveBallDown = SKAction.moveToY(293, duration: 0.4)
if ball.position.y == 600 {
self.ball.runAction(moveBallUp)
} else {
self.ball.runAction(moveBallDown)
}
}
}
I suggest you use repeatActionForever. If you start your ball position from the bottom of the screen this sequence should work. If you want to start the ball from the top then switch the positions of the actions in moveUpAndDown with each other.
func moveSprite() {
let moveBallUp = SKAction.moveToY(600, duration: 0.4)
let moveBallDown = SKAction.moveToY(293, duration: 0.4)
let moveUpAndDown = SKAction.sequence([moveBallUp, moveBallDown])
let moveUpAndDownForever = SKAction.repeatActionForever(moveUpAndDown)
ball.runAction(moveUpAndDownForever)
}

Collision Detection In Sprite Kit using Swift

The code below is from a project I am currently working on, I have been trying to teach myself Swift language and Sprite Kit for the past few days and this is my first attempt at a game, it is a Flappy Bird type game. I ran into a problem today when I was trying to write the code for the collision detection. When the bird touches one of the pipes the game is supposed to pause. However when I run the code and the bird touches the pipe, nothing happens, the bird just bounces off of it. I have read many tutorials and watched many videos on this subject to try and resolve my problem and haven't had any luck. I have written all of the collision detection code that I learned off of the last video I watched in the code below. Could anyone please tell me what I am doing wrong. Any advice would be greatly appreciated, thank you.
//
// GameScene.swift
// Bird Flappy Game
//
// Created by Brandon Ballard on 1/4/15.
// Copyright (c) 2015 Brandon Ballard. All rights reserved.
//
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var pipesMoveAndRemove = SKAction()
let pipeGap = 150.0
enum ColliderType:UInt32 {
case BIRD = 1
case PIPE = 2
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
backgroundColor = SKColor.cyanColor()
//physics
self.physicsWorld.gravity = CGVectorMake(0.0, -15.0);
self.physicsWorld.contactDelegate = self
func didBeginContact(contact: SKPhysicsContactDelegate) {
scene?.view?.paused = true
bird.setScale(12.0)
}
//Bird
var birdTexture = SKTexture(imageNamed:"Bird")
birdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: birdTexture)
bird.setScale(0.6)
bird.position = CGPoint(x: 375, y: self.frame.size.height * 0.6)
bird.physicsBody = SKPhysicsBody(circleOfRadius: bird.size.height / 2.0)
bird.physicsBody?.dynamic = true
bird.physicsBody?.allowsRotation = true
bird.physicsBody?.affectedByGravity = true
bird.physicsBody!.categoryBitMask = ColliderType.BIRD.rawValue
bird.physicsBody!.contactTestBitMask = ColliderType.PIPE.rawValue
bird.physicsBody!.collisionBitMask = ColliderType.PIPE.rawValue
self.addChild(bird)
//Ground
var groundTexture = SKTexture(imageNamed: "Ground")
var sprite = SKSpriteNode(texture: groundTexture)
sprite.setScale(2.0)
sprite.position = CGPointMake(self.size.width / 2, sprite.size.height / 2.0)
self.addChild(sprite)
var ground = SKNode()
ground.position = CGPointMake(0, groundTexture.size().height + 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width, groundTexture.size().height * 2.0))
ground.physicsBody?.dynamic = false
self.addChild(ground)
//Pipes
//Create the Pipes
pipeUpTexture = SKTexture(imageNamed: "PipeUp")
pipeDownTexture = SKTexture(imageNamed: "PipeDown")
//Movement of Pipes
let distanceToMove = CGFloat(self.frame.size.width + 2.0 * pipeUpTexture.size().width)
let movePipes = SKAction.moveByX(-distanceToMove, y: 0.0, duration: NSTimeInterval(0.01 * distanceToMove))
let removePipes = SKAction.removeFromParent()
pipesMoveAndRemove = SKAction.sequence([movePipes,removePipes])
//Spawn Pipes
let spawn = SKAction.runBlock({() in self.spawnPipes()})
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawnThenDelay = SKAction.sequence([spawn,delay])
let spawnThenDelayForever = SKAction.repeatActionForever(spawnThenDelay)
self.runAction(spawnThenDelayForever)
}
func spawnPipes() {
let pipePair = SKNode()
pipePair.position = CGPointMake(self.frame.size.width + pipeUpTexture.size().width * 2, 0)
pipePair.zPosition = -10
let height = UInt32(self.frame.size.height / 4)
let y = arc4random() % height + height
var pipeDown = SKSpriteNode(texture: pipeDownTexture)
pipeDown.setScale(2.0)////////
pipeDown.position = CGPointMake(3.0, CGFloat(y) + pipeDown.size.height + CGFloat(pipeGap) )
pipeDown.physicsBody = SKPhysicsBody(rectangleOfSize: pipeDown.size)
pipeDown.physicsBody?.dynamic = false
pipeDown.physicsBody!.affectedByGravity = false
pipeDown.physicsBody!.categoryBitMask = ColliderType.PIPE.rawValue
pipeDown.physicsBody!.contactTestBitMask = ColliderType.BIRD.rawValue
pipeDown.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
pipePair.addChild(pipeDown)
var pipeUp = SKSpriteNode(texture: pipeUpTexture)
pipeUp.setScale(2.0)
pipeUp.position = CGPointMake(0.0, CGFloat(y))
pipeUp.physicsBody = SKPhysicsBody(rectangleOfSize: pipeUp.size )
pipeUp.physicsBody?.dynamic = false
pipeUp.physicsBody!.affectedByGravity = false
pipeUp.physicsBody!.categoryBitMask = ColliderType.PIPE.rawValue
pipeUp.physicsBody!.contactTestBitMask = ColliderType.BIRD.rawValue
pipeUp.physicsBody!.collisionBitMask = ColliderType.BIRD.rawValue
pipePair.addChild(pipeUp)
pipePair.runAction(pipesMoveAndRemove)
self.addChild(pipePair)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
bird.physicsBody?.velocity = CGVectorMake( 0, 0 )
bird.physicsBody?.applyImpulse(CGVectorMake(0,25))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
From what I can see, you only need to DETECT collisions, not actually simulate them. For this, you need to set only the contactTestBitMask of the physicsBodies. You can set the collisionBitMask as 0.
bird.physicsBody!.collisionBitMask = 0
pipe.physicsBody!.collisionBitMask = 0
Also, as hamobi has already said, the didBeginContact method needs to be outside the didMoveToView method with the override keyword. (This question has the exact same problem as yours)
class GameScene: SKScene, SKPhysicsContactDelegate {
// ...
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
// set as delegate:
self.physicsWorld.contactDelegate = self
// ..
}
// should be called now
func didBeginContact(contact: SKPhysicsContact){
scene?.view?.paused = true
bird.setScale(12.0)
}
}
You put your didBeginContact INSIDE of didMoveToView. It's not callable from there. Put it in the body of your class