how can i change my spritekit on collision in spritekit swift.
else if collidedBird.birdType == 6 {
if UIDevice.currentDevice().userInterfaceIdiom == .Pad
{
_bird.physicsBody?.applyImpulse(CGVectorMake(0, birdImpluseReturn(70.0)))
} else {
_bird.physicsBody?.applyImpulse(CGVectorMake(0, birdImpluseReturn(70.0)))
}
let fire = NSTimer.scheduledTimerWithTimeInterval(0.05, target: self, selector: Selector("createDragonFire"), userInfo: nil, repeats: false)
playEffectSound("BS_Std_Jump_SFX.mp3")
_platform.removeFromParent()
}
If you want to change the Image of the Sprite, then you should use SKPhysicsContactDelegate Method.
In General that means that you have to implement the SKPhysicsContactDelegate, Make a new Struct:
For Example:
struct PhysicCategories {
static let NoneC : UInt32 = 0x1 << 0
static let Bird : UInt32 = 0x1 << 1
static let PlatformC : UInt32 = 0x1 << 2
static let FinishC : UInt32 = 0x1 << 3
static let ObstacleC : UInt32 = 0x1 << 4
}
then go in your didMoveToView() function and write:
PhysicsWorld.contactdelegate = self
Dont forget to make this Step!
Then add the func
func didBeginContact(contact: SKPhysicsContact) {
var contactbody1 = contact.bodyA
var contactbody2 = contact.bodyB
if contactbody1.categoryBitMask < contactbody2.categoryBitMask{
contactbody1 = contact.bodyA
contactbody2 = contact.bodyB
} else {
contactbody1 = contact.bodyB
contactbody2 = contact.bodyA
}
if ((contactbody1.categoryBitMask == PhysicCategories.BirdC) && (contactbody2.categoryBitMask == PhysicCategories.ObstacleC)){
// Make a GameOver Function for that!
// GameOver()
}
if ((contactbody1.categoryBitMask == PhysicCategories.BirdC) && (contactbody2.categoryBitMask == PhysicCategories.PlatformC)){
contactbody1.node.texture = SKTexture(imageNamed: "")
}
Related
I have made a game where the basic idea is that a player sprite has to either collide with or avoid falling enemy sprites depending on the letter assigned to the enemy. The problem is that when my player makes contact with a sprite, the surrounding enemies skip forward significantly. This makes the game too hard to play because there isn't enough time for the player to move out of the way of oncoming enemy sprites. This happens on the device and the simulator.
To see what I mean, look at this brief video. You can see the lag at 00:06 and 00:013: https://www.dropbox.com/s/8pp66baxc9uhy26/SimulatorScreenSnapz002.mov?dl=0
Here is my code for player and enemy contact:
class GameplaySceneClass: SKScene, SKPhysicsContactDelegate {
private var player:Player?
private var center = CGFloat()
private var canMove = false, moveLeft = false
private var itemController = ItemController()
private var scoreLabel: SKLabelNode?
private var wordLabel: SKLabelNode?
private var score = 0
private var theWord = ""
private var vowelPressed = false
let correct = SoundSFX("correct.wav")
let wrong = SoundSFX("nomatch.wav")
let explosion = SoundSFX("explosion.wav")
var arrayOfStrings: [String]?
var theCheckedWord = ""
var currentCount = 0
var itemsArray = [String]()
var partialExists = false
var characterScore = 0
var onePointLetters = [12, 14, 18, 19, 20]
var twoPointLetters = [4, 7]
var threePointLetters = [2, 3, 13, 16]
var fourPointLetters = [6, 8, 22, 23, 25]
var fivePointLetters = [11]
var eightPointLetters = [10, 24]
var tenPointLetters = [17, 26]
var letter = 0
var scoreArray = [Int]()
var matchingTerms = [String]()
var gravity:CGFloat = -0.35
var result = CGSize()
let synth = AVSpeechSynthesizer()
var myUtterance = AVSpeechUtterance(string:"")
override func didMove(to view: SKView) {
SoundEngine.shared.backgroundMusicVolume = 1.0
SoundEngine.shared.playBackgroundMusic("Jungle Audio-1.m4a", loop: true)
initializeGame()
}
override func update(_ currentTime: TimeInterval) {
managePlayer()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location (in: self)
if atPoint(location).name == "LetterA" {
print ("Letter A pressed")
vowelPressed = true
theWord = theWord + "A"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterE" {
print ("Letter E pressed")
vowelPressed = true
theWord = theWord + "E"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterI" {
print ("Letter I pressed")
vowelPressed = true
theWord = theWord + "I"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterO" {
print ("Letter O pressed")
vowelPressed = true
theWord = theWord + "O"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "LetterU" {
print ("Letter U pressed")
vowelPressed = true
theWord = theWord + "U"
wordLabel?.text = theWord
if theWord.characters.count >= 3 {
checkWord()
}
vowelPressed = false
}
else if atPoint(location).name == "Pause" {
showPauseAlert()
}
else if atPoint(location).name == "delete" {
theWord = ""
theCheckedWord = ""
wordLabel?.text = theWord
}
else {
if location.x > center && !vowelPressed {
moveLeft = false
}
else if location.x < center && !vowelPressed {
moveLeft = true
}
canMove = true
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
canMove = false
}
func didBegin(_ contact: SKPhysicsContact) {
if !gamePaused {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.node?.name == "Player" {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Tile" {
letter = secondBody.node?.userData?.value(forKey:"key") as! Int
print ("The value of letter is \(letter)")
var letterCode = String(describing: letter)
var letterValue = Int(letterCode)
var finalLetterValue = letterValue!+64
var ASCIICode = Character(UnicodeScalar(finalLetterValue)!)
var letterString = String(describing:ASCIICode)
theWord = theWord + letterString
print (theWord)
if onePointLetters.contains(letter) {
characterScore += 1
}
else if twoPointLetters.contains(letter) {
characterScore += 2
}
else if threePointLetters.contains(letter) {
characterScore += 3
}
else if fourPointLetters.contains(letter) {
characterScore += 4
}
else if fivePointLetters.contains(letter) {
characterScore += 5
}
else if eightPointLetters.contains(letter) {
characterScore += 8
}
else if tenPointLetters.contains(letter) {
characterScore += 10
}
wordLabel?.text = theWord
theCheckedWord = theWord.lowercased()
print ("The checked word is \(theCheckedWord)")
checkWord()
secondBody.node?.removeFromParent()
}
if firstBody.node?.name == "Player" && secondBody.node?.name == "Bomb" {
explosion.play()
firstBody.node?.removeFromParent()
secondBody.node?.removeFromParent()
theWord = ""
score = 0
scoreLabel?.text = String(score)
var delayTimer = SKAction.wait(forDuration:1)
run (delayTimer)
restartGame()
}
}
}
private func initializeGame() {
do {
// This solution assumes you've got the file in your bundle
if let path = Bundle.main.path(forResource: "en", ofType: "txt"){
let data = try String(contentsOfFile:path, encoding: String.Encoding.utf8)
arrayOfStrings = data.components(separatedBy: "\n")
}
} catch let err as NSError {
// do something with Error
print(err)
}
physicsWorld.contactDelegate = self
physicsWorld.gravity = CGVector(dx:0,dy:gravity)
player = childNode(withName: "Player") as? Player!
player?.initializePlayer()
player?.position = CGPoint(x:0, y: -420)
scoreLabel = childNode(withName: "ScoreLabel") as? SKLabelNode!
scoreLabel?.text = String(score)
wordLabel = childNode(withName:"WordLabel") as? SKLabelNode!
wordLabel?.text = ""
center = self.frame.size.width/self.frame.size.height
var spawnBlocks = SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration:1),SKAction.run(spawnItems)]))
var removeBlocks = SKAction.repeatForever(SKAction.sequence([SKAction.wait(forDuration:1),SKAction.run(removeItems)]))
if gamePaused == true {
self.scene?.isPaused = true
}
self.run(spawnBlocks)
self.run(removeBlocks)
}
private func checkWord() {
theCheckedWord = theWord.lowercased()
if (arrayOfStrings?.contains(theCheckedWord))! {
print ("Yes! \(theCheckedWord) is a word")
correct.play()
print ("The current value of score is \(score)")
score += characterScore
print ("Current gravity setting is \(gravity)")
scoreLabel?.text = String(score)
characterScore = 0
matchingTerms = (arrayOfStrings?.filter({$0.hasPrefix(theCheckedWord)
}))!
if matchingTerms.count == 1 {
characterScore = 0
print ("Current gravity setting is \(gravity)")
theWord = ""
theCheckedWord = ""
wordLabel?.text = ""
}
}
else if !(arrayOfStrings?.contains(theCheckedWord))! {
matchingTerms = (arrayOfStrings?.filter({$0.hasPrefix(theCheckedWord)
}))!
if matchingTerms.count == 0 {
wrong.play()
characterScore = 0
if score >= 5 {
score -= 5
}
theWord = ""
theCheckedWord = ""
wordLabel?.text = ""
}
}
}
Here is my code for the enemies:
import SpriteKit
struct ColliderType {
static let PLAYER: UInt32 = 0
static let TILE_AND_BOMB: UInt32 = 1;
}
public var bounds = UIScreen.main.bounds
public var width = bounds.size.width
public var height = bounds.size.height
class ItemController {
private var minX = CGFloat(-(width/2)), maxX = CGFloat(width/2)
private var vowelNumbers = [1, 5, 9, 15, 21]
func spawnItems() -> SKSpriteNode {
let item: SKSpriteNode?
if Int(randomBetweenNumbers(firstNum: 0, secondNum: 10)) > 7 {
item = SKSpriteNode(imageNamed: "Bomb")
item!.name = "Bomb"
item!.setScale(0.6)
item!.physicsBody = SKPhysicsBody(circleOfRadius: item!.size.height / 2)
print ("The value of width is \(width)")
} else {
var num = Int(randomBetweenNumbers(firstNum: 1, secondNum: 26))
if vowelNumbers.contains(num) {
num = num + 1
}
item = SKSpriteNode(imageNamed: "Tile \(num)")
item?.userData = NSMutableDictionary()
item!.name = "Tile"
item!.userData!.setValue(num, forKey:"key")
item!.zRotation = 0
item!.setScale(0.9)
item!.physicsBody = SKPhysicsBody(circleOfRadius: item!.size.height / 2)
if gamePaused == true {
item!.isPaused = true
}
else {
item!.isPaused = false
}
}
item!.physicsBody?.categoryBitMask = ColliderType.TILE_AND_BOMB;
item!.physicsBody?.isDynamic = true
item!.zPosition = 3
item!.anchorPoint = CGPoint(x: 0.5, y: 0.5)
item!.position.x = randomBetweenNumbers(firstNum: minX, secondNum: maxX)
item!.position.y = 600
return item!
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat {
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum);
}
}
And here is my code for the player:
import SpriteKit
class Player: SKSpriteNode {
private var minX = CGFloat(-300), maxX = CGFloat(300)
func initializePlayer() {
name = "Player"
physicsBody = SKPhysicsBody(circleOfRadius: size.height/2)
physicsBody?.affectedByGravity = false
physicsBody?.isDynamic = false
physicsBody?.categoryBitMask = ColliderType.PLAYER
physicsBody?.contactTestBitMask = ColliderType.TILE_AND_BOMB
}
func move(left:Bool){
if left {
position.x -= 15
if position.x < minX {
position.x = minX
}
}
else {
position.x += 15
if position.x > maxX {
position.x = maxX
}
}
}
}
Consider calling the checkWord() method from an SKAction:
let checkWordsAction = SKAction.run() {
[weak self] in
self?.checkWord()
}
self.run(checkWordsAction)
That way, if your array is too big, the code will not get interrupted because you are walking it to check for your word.
Also I'm not sure of what you are trying to do with the wait action at the end of the didBegin(contact:) method, when you are colliding with bombs. If you are trying to wait for 1 second before restarting the game, then keep in mind that calling the wait action the way you do will not pause your code. Actually it will simply put the scene on idle for 1 second and restart the game in the background. If that is the intended effect, it's ok, but you might be better using the run(_:completion:) method instead and call restartGame() from the completion closure:
let waitAction = SKAction.wait(1)
self.run(waitAction) { [weak self] in
self?.restartGame()
}
Hope this helps!
In didBegin(_ contact: SKPhysicsContact), you create two physics bodies in:
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
Maybe the lag is created because you create two new physics bodies. What if you don't create the physics bodies, but just create references to existing ones? I haven't tested this, but maybe try:
let firstBody: SKPhysicsBody
let secondBody: SKPhysicsBody
if contact.bodyA.node?.name == "Player" {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
I am trying to delete an enemy after 2 shots.Here is my didBegin
func didBegin(_ contact: SKPhysicsContact) {
var body1:SKPhysicsBody
var body2:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
}else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == PhysicsCatagory.Bullet && body2.categoryBitMask == PhysicsCatagory.Enemy{
//if the bullet has hit the enemy
if body2.node != nil {
spawnSplatter(spawnPosition: body2.node!.position)
body1.node?.removeFromParent()
body2.node?.removeFromParent()
}
This deletes an enemy after being hit once, can someone tell me how I can delete an enemy after 2 hits?
Usually, try to remove nodes to didBegin(_ contact: it's not a good idea because you could have multiple contacts with nodes that become nil after the first contact and this could cause a crash.
var bulletCounter : Int = 0
var nodesToRemove = [SKNode]()
func didBegin(_ contact: SKPhysicsContact) {
var body1:SKPhysicsBody
var body2:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
body1 = contact.bodyA
body2 = contact.bodyB
}else{
body1 = contact.bodyB
body2 = contact.bodyA
}
if body1.categoryBitMask == PhysicsCatagory.Bullet && body2.categoryBitMask == PhysicsCatagory.Enemy{
//if the bullet has hit the enemy
bulletCounter += 1
nodesToRemove.append(body1.node!) // remove always the bullet
switch bulletCounter {
case 2:
nodesToRemove.append(body2.node!) // remove enemy
bulletCounter = 0
default:break
}
}
}
override func didFinishUpdate()
{
nodesToRemove.forEach(){$0.removeFromParent()}
nodesToRemove = [SKNode]()
}
This example can be useful if you have 1 enemy.
If you have more enemies you can create a bulletCounter property to your Enemy class or store a bulletCounter value inside the SKNode userData property:
Create the dictionary first:
enemy.userData = NSMutableDictionary()
enemy.userData?.setObject(0, forKey: "bulletCounter")
Get/assign your value during the game:
if let bulletCounter = self.userData?.object(forKey: "bulletCounter") {
var counter = bulletCounter as! Int
counter += 1
self.userData?.setObject(counter, forKey: "bulletCounter" as NSCopying)
}
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
So I am creating an air hockey game and there are 5 major SKSpriteNodes. I need to detect collision between the puck and goal1 and goal2. When it does detect collision it needs to add 1 to the scoreLeft or scoreRight which are SKLabelNode's. I have used the Gamescene.sks file to make the visual representation. I commented the didBeginContact because thats where it doesn't work. Thank You in advance.
class GameScene: SKScene, SKPhysicsContactDelegate {
let paddle1CategoryName = "paddle1"
let paddle2CategoryName = "paddle2"
let puckCategoryName = "puck"
let goal1CategoryName = "goal1"
let goal2CategoryName = "goal2"
let scoreLeftCategoryName = "scoreLeft"
let scoreRightCategoryName = "scoreRight"
var isFingerOnPaddle1 = false
var isFingerOnPaddle2 = false
var paddle1Category: UInt32 = 0x1 << 0
var paddle2Category: UInt32 = 0x1 << 1
var puckCategory: UInt32 = 0x1 << 2
var goal1Category: UInt32 = 0x1 << 3
var goal2Category: UInt32 = 0x1 << 4
override func didMoveToView(view: SKView) {
/* Setup your scene here */
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
let paddle1 = childNodeWithName(paddle1CategoryName) as! SKSpriteNode
let paddle2 = childNodeWithName(paddle2CategoryName) as! SKSpriteNode
let puck = childNodeWithName(puckCategoryName) as! SKSpriteNode
let goal1 = childNodeWithName(goal1CategoryName) as! SKSpriteNode
let goal2 = childNodeWithName(goal2CategoryName) as! SKSpriteNode
let scoreLeft = childNodeWithName(scoreLeftCategoryName) as! SKLabelNode
let scoreRight = childNodeWithName(scoreRightCategoryName) as! SKLabelNode
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
var touch = touches.first! as UITouch
var location = touch.locationInNode(self)
if let body = physicsWorld.bodyAtPoint(location){
if body.node!.name == paddle1CategoryName {
print("The game has begun.")
isFingerOnPaddle1 = true
} else if body.node!.name == paddle2CategoryName{
print("The game has begun.")
isFingerOnPaddle2 = true
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
let paddle1 = childNodeWithName(paddle1CategoryName) as! SKSpriteNode
let paddle2 = childNodeWithName(paddle2CategoryName) as! SKSpriteNode
let puck = childNodeWithName(puckCategoryName) as! SKSpriteNode
let goal1 = childNodeWithName(goal1CategoryName) as! SKSpriteNode
let goal2 = childNodeWithName(goal2CategoryName) as! SKSpriteNode
var touch = touches.first! as UITouch
var touchLocation = touch.locationInNode(self)
var previousLocation = touch.previousLocationInNode(self)
if isFingerOnPaddle1{
let paddle1X = paddle1.position.x + (touchLocation.x - previousLocation.x)
let paddle1Y = paddle1.position.y + (touchLocation.y - previousLocation.y)
if (touchLocation.x <= paddle1.position.x && (touchLocation.y <= paddle1.position.y || touchLocation.y >= paddle1.position.y)){
paddle1.position = CGPoint(x: paddle1X, y: paddle1Y)
}
}
if isFingerOnPaddle2{
let paddle2X = paddle2.position.x + (touchLocation.x - previousLocation.x)
let paddle2Y = paddle2.position.y + (touchLocation.y - previousLocation.y)
if (touchLocation.x >= paddle2X && (touchLocation.y >= paddle2Y || touchLocation.y < paddle2.position.y)){
paddle2.position = CGPoint(x: paddle2X, y: paddle2Y)
}
}
}
/*
func didBeginContact(contact: SKPhysicsContact) {
let scoreLeft = childNodeWithName(scoreLeftCategoryName) as! SKLabelNode
let scoreRight = childNodeWithName(scoreRightCategoryName) as! SKLabelNode
var score = 0
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask{
case puckCategory | goal1Category:
if contact.bodyA.categoryBitMask == puckCategory{
puckCategory = contact.bodyA.categoryBitMask
goal1Category = contact.bodyB.categoryBitMask
score++
scoreRight.text = "\(score)"
}else{
puckCategory = contact.bodyB.categoryBitMask
goal1Category = contact.bodyA.categoryBitMask
score++
scoreRight.text = "\(score)"
}
case puckCategory | goal2Category:
if contact.bodyA.categoryBitMask == puckCategory{
puckCategory = contact.bodyA.categoryBitMask
goal2Category = contact.bodyB.categoryBitMask
score++
scoreLeft.text = "\(score)"
}else{
puckCategory = contact.bodyB.categoryBitMask
goal2Category = contact.bodyA.categoryBitMask
score++
scoreLeft.text = "\(score)"
}
default:
// Nobody expects this, so satisfy the compiler and catch
// ourselves if we do something we didn't plan to
fatalError("other collision: \(contactMask)")
}
}*/
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
One issue is using a local var score = 0 at the beginning of your function. Remove it. The score should be stored in the game's model so that logic about winning can be applied but matching your code, at least do this.
if let score = Int(scoreLeft.text) {
scoreLeft.text = "\(score + 1)"
}
Additionally there is a problem with
case puckCategory | goal1Category:
if contact.bodyA.categoryBitMask == puckCategory{
puckCategory = contact.bodyA.categoryBitMask
What you are saying is if contact.bodyA.categoryBitMask is equal to puckCategory, then set puckCategory to contact.bodyA.categoryBitMask. That is a no-op, nothing changes AND more importantly you don't want to change the bit masks that your child nodes are compared against. Make puckCategory, goal1Categoy, and goal2Category constants. Good luck.
Follow Up To OP Comment:
So, throw away the code in didBeginContact that set the category mask variables for two reasons. 1) Setting variables like puckCatergory to their same value is wasteful and confusing. 2) Since variables like puckCategory are compared to child node bit masks, it would be disasterous if they ever did change. That function should look like this. Also be sure and add handlers for the other types of contacts before you get too far.
func didBeginContact(contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case puckCategory | goal1Category:
let scoreRight = childNodeWithName(scoreRightCategoryName) as! SKLabelNode
if let score = Int(scoreRight.text) {
scoreRight.text = "\(score + 1)"
}
case puckCategory | goal2Category:
let scoreLeft = childNodeWithName(scoreLeftCategoryName) as! SKLabelNode
if let score = Int(scoreLeft.text) {
scoreLeft.text = "\(score + 1)"
}
default:
// There are other contacts that need to be handled; paddle+puck,puck+wall, etc
fatalError("other collision: \(contactMask)")
}
}
I have a problem with my func contact because the second contact don't work (contact with Bonus and Vaisseau) but the two others contact don't work.
So this is my code :
The function :
func didBeginContact(contact: SKPhysicsContact) {
let PremierBody : SKPhysicsBody = contact.bodyA
let SecondBody : SKPhysicsBody = contact.bodyB
if ((PremierBody.categoryBitMask == PhysicsCategories.Meteorites) && (SecondBody.categoryBitMask == PhysicsCategories.Meteorites)) {
contactEntreMeteorites(PremierBody.node as! SKSpriteNode, Meteorites2: SecondBody.node as! SKSpriteNode)
}
else if ((PremierBody.categoryBitMask == PhysicsCategories.Bonus) && (SecondBody.categoryBitMask == PhysicsCategories.Vaisseau) ||
(PremierBody.categoryBitMask == PhysicsCategories.Vaisseau) && (SecondBody.categoryBitMask == PhysicsCategories.Bonus)){
gameOver(PremierBody.node as! SKSpriteNode, Vaisseau: SecondBody.node as! SKSpriteNode)
print("CONTACT")
}
else if ((PremierBody.categoryBitMask == PhysicsCategories.Meteorites) && (SecondBody.categoryBitMask == PhysicsCategories.Vaisseau) ||
(PremierBody.categoryBitMask == PhysicsCategories.Vaisseau) && (SecondBody.categoryBitMask == PhysicsCategories.Meteorites)){
gameOver(PremierBody.node as! SKSpriteNode, Vaisseau: SecondBody.node as! SKSpriteNode)
print("Couco")
}
}
And this is my Physics categories :
struct PhysicsCategories {
static let Meteorites : UInt32 = 1
static let Bonus : UInt32 = 2
static let Vaisseau : UInt32 = 5
}
This in my func DidMoveToView :
Vaisseau = SKSpriteNode(texture: Vaisseau1)
Vaisseau.position = CGPointMake(self.frame.size.width / 2, self.frame.size.height / 2)
Vaisseau.physicsBody = SKPhysicsBody(rectangleOfSize: Vaisseau.size)
Vaisseau.physicsBody?.affectedByGravity = false
Vaisseau.physicsBody?.categoryBitMask = PhysicsCategories.Vaisseau
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Bonus
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Meteorites
Vaisseau.physicsBody?.dynamic = false
Vaisseau.setScale(0.08)
self.addChild(Vaisseau)
And i create my SpriteNode Bonus in this function :
func ApparitionBonus() {
let BonusSprite = SKSpriteNode(imageNamed: "Bonus.png")
var BonusApparitionX = UInt32(self.frame.size.width)
var BonusApparitionY = UInt32(self.frame.size.height)
BonusApparitionX = arc4random() % BonusApparitionX
BonusApparitionY = arc4random() % BonusApparitionY
BonusSprite.position = CGPointMake(CGFloat(BonusApparitionX),CGFloat(BonusApparitionY))
BonusSprite.setScale(0.8)
BonusSprite.physicsBody = SKPhysicsBody(circleOfRadius: 20)
BonusSprite.physicsBody?.affectedByGravity = false
BonusSprite.physicsBody?.categoryBitMask = PhysicsCategories.Bonus
BonusSprite.physicsBody?.contactTestBitMask = PhysicsCategories.Vaisseau
BonusSprite.physicsBody?.dynamic = false
self.addChild(BonusSprite)
let RotationBonus = SKAction.rotateByAngle(CGFloat(M_PI), duration: 3)
let wait = SKAction.waitForDuration(3)
let actionFini = SKAction.removeFromParent()
BonusSprite.runAction(SKAction.sequence([RotationBonus, wait, actionFini]))
BonusSprite.runAction(SKAction.repeatActionForever(RotationBonus))
}
In your didMoveToView, when you call:
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Bonus
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Meteorites
You set the contactTestBitMask to one category, and then another. You need to bitwise or them together. Something like:
Vaisseau.physicsBody?.contactTestBitMask = PhysicsCategories.Bonus | PhysicsCategories.Meteorites
This will combine the two bit masks, which will allow it to contact both the Bonus and Meteorites.
If you don't want your sprites to collide, set the collisionBitMask to 0x0 (or however you want to represent 0):
Vaisseau.physicsBody?.collisionBitMask = 0x0