SpriteKit & Swift 3 - Bounce Not Working Properly With Ball Node - swift

I made a game which essentially has a ball and a couple of targets and paddles. However I needed to make it so depending on what target the ball collides with the speed is either tripled, doubled or halved as long as it doesn't drop below a minimum speed or goes above a maximum speed.
To do this I used the didBegin(contact) function together and set the different velocities as it can be seen in the code below:
extension CGVector {
var speed: CGFloat {
return hypot(dx, dy)
}
static func > (lhs: CGVector, rhs: CGVector) -> Bool {
return lhs.speed > rhs.speed
}
static func < (lhs: CGVector, rhs: CGVector) -> Bool {
return lhs.speed < rhs.speed
}
static func * (vector: CGVector, multiplier: CGFloat) -> CGVector {
return CGVector(dx: vector.dx * multiplier, dy: vector.dy * multiplier)
}
static func / (vector:CGVector, divider:CGFloat) -> CGVector {
return CGVector(dx: vector.dx / divider, dy: vector.dy / divider)
}
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
let ballNode = self.childNode(withName: ballName)
let minSpeed = self.initialSpeed / 2
let maxSpeed = self.initialSpeed * 3
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
} else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask == ballBitmask && secondBody.categoryBitMask == drainBitmask {
endGame()
Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(GameScene.showLeaderboard), userInfo: nil, repeats: false)
} else if firstBody.categoryBitMask == ballBitmask && secondBody.categoryBitMask == target1Bitmask {
let currentSpeed = ballNode?.physicsBody?.velocity
score += 20
self.vc.scoreLabel.text = "Score: \(score)"
if currentSpeed == maxSpeed {
ballNode?.physicsBody?.velocity = currentSpeed!
} else if (currentSpeed! * 2) > maxSpeed {
ballNode?.physicsBody?.velocity = maxSpeed
} else if (currentSpeed! * 2) < maxSpeed {
ballNode?.physicsBody?.velocity = currentSpeed! * 2
}
} else if firstBody.categoryBitMask == ballBitmask && secondBody.categoryBitMask == target2Bitmask {
let currentSpeed = ballNode?.physicsBody?.velocity
score += 10
self.vc.scoreLabel.text = "Score: \(score)"
if currentSpeed == minSpeed {
ballNode?.physicsBody?.velocity = currentSpeed!
} else if (currentSpeed! / 2) > minSpeed {
ballNode?.physicsBody?.velocity = currentSpeed! / 2
} else if (currentSpeed! / 2) < minSpeed {
ballNode?.physicsBody?.velocity = minSpeed
}
} else if firstBody.categoryBitMask == ballBitmask && secondBody.categoryBitMask == target3Bitmask {
let currentSpeed = ballNode?.physicsBody?.velocity
score += 30
self.vc.scoreLabel.text = "Score: \(score)"
if currentSpeed == maxSpeed {
ballNode?.physicsBody?.velocity = currentSpeed!
} else if (currentSpeed! * 3) > maxSpeed {
ballNode?.physicsBody?.velocity = maxSpeed
} else if (currentSpeed! * 3) < maxSpeed {
ballNode?.physicsBody?.velocity = currentSpeed! * 3
}
}
My issue is sometimes when the ball collides with the targets it bounces off in a very weird and unrealistic way and sometimes just essentially glitches out (please check the link below for a short video clip which shows what I mean)
Note: At 2/3 seconds into the video is an example of the weird bounce happening
https://drive.google.com/open?id=0BxgQmn_JruJ_aUU2dVBoVlprV0k
Why is this happening and more importantly how can I fix it?
PS: One suggestion that I believe might be causing this is the fact the vector for velocity controls both the angle(direction) and speed (I think)of the ball therefore when I set the velocity the direction isn't taken in account. If this is the cause would it be possible to triple/double/halve the velocity whilst still making the bounce realistic?

Related

Swift programming. Possible problem with SKPhysicscontact

I'm working on a project / game and I'm stuck at adding new enemies to the game and to program them. This is how my code looked before I started changing and only had one type of "alien". I want to create two new types, one that you lose 5 points from hitting and one 10. In the code now, there is only one of three and it gives +5 points. How would you guys add the two new? I think I'm overthinking it and that's why I messed up.
import SpriteKit
import GameplayKit
import CoreMotion
class GameScene: SKScene, SKPhysicsContactDelegate {
var starfield:SKEmitterNode!
var player:SKSpriteNode!
var scoreLabel:SKLabelNode!
var score:Int = 0 {
didSet {
scoreLabel.text = "Score: \(score)"
}
}
var gameTimer:Timer!
var possibleAliens = ["alien"]
//This is the "alien you earn +5 point from shooting. Im trying to get 2 new aliens to the scene, one that you lose -5 points from shooting and one you lose -10 on.
//I have tried to rewrite the codes to the two new, but they still earn +5 points from shooting all 3 types. What would you guys do and how?
let alienCategory:UInt32 = 0x1 << 1
let photonTorpedoCategory:UInt32 = 0x1 << 0
let motionManger = CMMotionManager()
var xAcceleration:CGFloat = 0
override func didMove(to view: SKView) {
starfield = SKEmitterNode(fileNamed: "Starfield")
starfield.position = CGPoint(x: 0, y: 1472)
starfield.advanceSimulationTime(10)
self.addChild(starfield)
starfield.zPosition = -1
player = SKSpriteNode(imageNamed: "shuttle")
player.position = CGPoint(x: self.frame.size.width / 2, y: player.size.height / 2 + 20)
self.addChild(player)
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
self.physicsWorld.contactDelegate = self
scoreLabel = SKLabelNode(text: "Score: 0")
scoreLabel.position = CGPoint(x: 100, y: self.frame.size.height - 60)
scoreLabel.fontName = "AmericanTypewriter-Bold"
scoreLabel.fontSize = 36
scoreLabel.fontColor = UIColor.white
score = 0
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
motionManger.accelerometerUpdateInterval = 0.2
motionManger.startAccelerometerUpdates(to: OperationQueue.current!) { (data:CMAccelerometerData?, error:Error?) in
if let accelerometerData = data {
let acceleration = accelerometerData.acceleration
self.xAcceleration = CGFloat(acceleration.x) * 0.75 + self.xAcceleration * 0.25
}
}
}
func addAlien () {
possibleAliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: possibleAliens) as! [String]
let alien = SKSpriteNode(imageNamed: possibleAliens[0])
let randomAlienPosition = GKRandomDistribution(lowestValue: 0, highestValue: 414)
let position = CGFloat(randomAlienPosition.nextInt())
alien.position = CGPoint(x: position, y: self.frame.size.height + alien.size.height)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategory
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let animationDuration:TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -alien.size.height), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
alien.run(SKAction.sequence(actionArray))
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
fireTorpedo()
}
func fireTorpedo() {
self.run(SKAction.playSoundFileNamed("torpedo.mp3", waitForCompletion: false))
let torpedoNode = SKSpriteNode(imageNamed: "torpedo")
torpedoNode.position = player.position
torpedoNode.position.y += 5
torpedoNode.physicsBody = SKPhysicsBody(circleOfRadius: torpedoNode.size.width / 2)
torpedoNode.physicsBody?.isDynamic = true
torpedoNode.physicsBody?.categoryBitMask = photonTorpedoCategory
torpedoNode.physicsBody?.contactTestBitMask = alienCategory
torpedoNode.physicsBody?.collisionBitMask = 0
torpedoNode.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(torpedoNode)
let animationDuration:TimeInterval = 0.3
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: player.position.x, y: self.frame.size.height + 10), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
torpedoNode.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & photonTorpedoCategory) != 0 && (secondBody.categoryBitMask & alienCategory) != 0 {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
}
}
func torpedoDidCollideWithAlien (torpedoNode:SKSpriteNode, alienNode:SKSpriteNode) {
let explosion = SKEmitterNode(fileNamed: "Explosion")!
explosion.position = alienNode.position
self.addChild(explosion)
self.run(SKAction.playSoundFileNamed("explosion.mp3", waitForCompletion: false))
torpedoNode.removeFromParent()
alienNode.removeFromParent()
self.run(SKAction.wait(forDuration: 2)) {
explosion.removeFromParent()
}
score += 5
}
override func didSimulatePhysics() {
player.position.x += xAcceleration * 50
if player.position.x < -20 {
player.position = CGPoint(x: self.size.width + 20, y: player.position.y)
}else if player.position.x > self.size.width + 20 {
player.position = CGPoint(x: -20, y: player.position.y)
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I wouldn't recommend following this answer if your game becomes more complex. Normally I would say put the aliens in subclasses initialized with their own categorybitmasks and other properties, but to avoid modifying your code too much I just changed the addAlien() method and didBeginContact method. Each alien has its own categoryBitMask and textureName.
let alienCategories:[UInt32] = [(0x1 << 1),(0x1 << 2),(0x1 << 3)]
let alienTextureNames:[String] = ["alien1","alien2","alien3"]
This will make a random Alien using the values in the collections above
func addAlien () {
//Random index between 0 and 2 changing the texture and category bitmask
let index = Int.random(in: 0...2)
let textureName = alienTextureNames[index]
let alien = SKSpriteNode(imageNamed: textureName)
//Use Int.random() if you don't want a CGFLoat
let xPos = CGFloat.random(in: 0...414)
let yPos = frame.size.height + alien.size.height
alien.position = CGPoint(x: xPos, y: yPos)
alien.physicsBody = SKPhysicsBody(rectangleOf: alien.size)
alien.physicsBody?.isDynamic = true
alien.physicsBody?.categoryBitMask = alienCategories[index]
alien.physicsBody?.contactTestBitMask = photonTorpedoCategory
alien.physicsBody?.collisionBitMask = 0
self.addChild(alien)
let moveAction = SKAction.moveTo(y: -alien.size.height, duration: 6)
alien.run(moveAction, completion: { alien.removeFromParent() })
}
func didBegin(_ contact: SKPhysicsContact) {
var firstBody:SKPhysicsBody
var secondBody:SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}else{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[0] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score += 5
}else if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[1] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score -= 5
}else if firstBody.categoryBitMask == photonTorpedoCategory && secondBody.categoryBitMask == alienCategories[2] {
torpedoDidCollideWithAlien(torpedoNode: firstBody.node as! SKSpriteNode, alienNode: secondBody.node as! SKSpriteNode)
score -= 10
}
}
Change the torpedoNode.physicsBody?.contactTestBitMask = alienCategory line in the fireTorpedo method to alien.physicsBody?.contactTestBitMask = (alienCategories[0] | alienCategories[1] | alienCategories[2])

Swift SpriteKit contact detection check fail (always hit regardless of categoryBitMask)

I try to make objects moving down the screen with 2 categories. On the bottom I have 2 cars (left and right), and if the objects hit the car 1 is a "collision" and the other is a "spinner" that will spinn the car around....
Object setup have a random of "collision" and "spinner" object....
Only "spinner" objects should be detected now as I not added "collision" yet.
My problem is that all are treated the same, so spinner and collision objects get "Hit right car! / Hit left car!" in my console.
"Hit left tree! / "Hit right tree!" never triggers.
Code:
func didBegin(_ contact: SKPhysicsContact) {
if enableContact == true {
var firstBody = SKPhysicsBody()
var secondBody = SKPhysicsBody()
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if (firstBody.categoryBitMask & PC.L_CAR) != 0 && (secondBody.categoryBitMask & PC.L_COLLIDER) != 0 {
print ("Hit left car!")
}
else if (firstBody.categoryBitMask & PC.R_CAR) != 0 && (secondBody.categoryBitMask & PC.R_COLLIDER) != 0 {
print ("Hit right car!")
}
else if (firstBody.categoryBitMask & PC.L_CAR) != 0 && (secondBody.categoryBitMask & PC.L_SPINNER) != 0 {
print("Hit left tree!")
}
else if (firstBody.categoryBitMask & PC.R_CAR) != 0 && (secondBody.categoryBitMask & PC.R_SPINNER) != 0 {
print("Hit right tree!")
}
}}
Car setup:
func setUp() {
canMove = true
leftCar = self.childNode(withName: "leftCar") as! SKSpriteNode
rightCar = self.childNode(withName: "rightCar") as! SKSpriteNode
centerPoint = self.frame.size.width / self.frame.size.height
leftCar.physicsBody?.isDynamic = true
leftCar.physicsBody?.categoryBitMask = PC.L_CAR
leftCar.physicsBody?.contactTestBitMask = PC.L_SPINNER
leftCar.physicsBody?.collisionBitMask = 0
rightCar.physicsBody?.isDynamic = true
rightCar.physicsBody?.categoryBitMask = PC.R_CAR
rightCar.physicsBody?.contactTestBitMask = PC.R_SPINNER
rightCar.physicsBody?.collisionBitMask = 0
Object setup:
#objc func leftTraffic() {
if !stopEverything {
let leftTrafficItem : SKSpriteNode!
let randonNumber = Helper().randomBetweenTwoNumbers(firstNumber: 1, secondNumber: 13)
switch Int(randonNumber) {
case 1...4:
leftTrafficItem = SKSpriteNode(imageNamed: "orangeCar")
leftTrafficItem.name = "orangeCar"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
break
case 5...8:
leftTrafficItem = SKSpriteNode(imageNamed: "greenCar")
leftTrafficItem.name = "greenCar"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
break
case 9...11:
leftTrafficItem = SKSpriteNode(imageNamed: "rock")
leftTrafficItem.name = "rock"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
break
case 12...13:
leftTrafficItem = SKSpriteNode(imageNamed: "tree")
leftTrafficItem.name = "tree"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_SPINNER
break
default:
leftTrafficItem = SKSpriteNode(imageNamed: "orangCar")
leftTrafficItem.name = "orangeCar"
leftTrafficItem.physicsBody?.categoryBitMask = PC.L_COLLIDER
}
leftTrafficItem.anchorPoint = CGPoint(x: 0.5, y: 0.5)
leftTrafficItem.zPosition = 10
let randomNum = Helper().randomBetweenTwoNumbers(firstNumber: 1, secondNumber: 10)
switch Int(randomNum) {
case 1...4:
leftTrafficItem.position.x = -280
break
case 5...10:
leftTrafficItem.position.x = -100
break
default:
leftTrafficItem.position.x = -280
}
leftTrafficItem.position.y = 700
leftTrafficItem.physicsBody = SKPhysicsBody(texture: leftTrafficItem.texture!, size: (leftTrafficItem.texture?.size())!)
leftTrafficItem.physicsBody?.isDynamic = true
leftTrafficItem.physicsBody?.contactTestBitMask = 0
leftTrafficItem.physicsBody?.collisionBitMask = 0
leftTrafficItem.physicsBody?.affectedByGravity = false
addChild(leftTrafficItem)
}
}

Too many Enemies Spawning, and not colliding with Player

So I added this recent code (to keep enemy nodes in the borders of the screen) with help from #Fluidity from my last question, now my issue is that with this code, TOO many enemies are coming down, and they're not colliding with either the bullets the Player shoots, or the Player nodes themselves, like it was previously. Here's some code:
func createEnemy() {
let wait:SKAction = SKAction.wait(forDuration: 1.25)
//1.0 = 1.35
let block:SKAction = SKAction.run(launchEnemy)
let seq:SKAction = SKAction.sequence( [ wait, block ])
let repeated:SKAction = SKAction.repeatForever(seq)
self.run(repeated, withKey:"enemyLaunchAction")
self.run(seq, withKey:"EnemySpawnAction")
}
func launchEnemy() {
let EnemyMissile:EnemyClass = EnemyClass()
EnemyMissile.createEnemy("Enemy")
addChild(EnemyMissile)
let enemy = EnemyMissile.EnemyNode
// let randomX = arc4random_uniform( UInt32(screenWidth))
var randomX = CGFloat(arc4random_uniform(UInt32(self.size.width))) // This is not a perfect rando algo.
// Because our random x was from 0 through screenwidth, but .position works
// on -halfWidth through +halfWidth
randomX -= (self.frame.size.width / 2)
// Because spawning on the border will mean that the enemy is HALF out of view:
/*
right border: |
enemy # frame.maxX: x
- offset: x|
- size.w/2 x |
*/
enemy.position = CGPoint( x: CGFloat(randomX) - (screenWidth / 2), y: screenHeight + 50)
let action = SKAction.move(by: CGVector(dx: 0, dy: -400 + speedScore), duration: 5.0)
enemy.run(SKAction.repeatForever(action))
increaseSpeed()
print("enemy spawned!")
self.run(action, withKey:"LaunchEnemyAction")
let offset = CGFloat(5) // pixels, so enemy will have some spacing between borders.
if randomX > self.frame.maxX - offset - (enemy.size.width / 2) {
randomX = self.frame.maxX - offset - (enemy.size.width / 2)
}
else if randomX < self.frame.minX + offset + (enemy.size.width / 2) {
randomX = self.frame.minX + offset + (enemy.size.width / 2)
}
enemy.position.x = randomX
}
func keepEnemyInBounds() {
// A better way to do this would be to have a dictionary of enemies:
for node in self.children {
guard node.name == "enemy" else { continue }
let enemy = node as! SKSpriteNode
if enemy.position.x > frame.maxX - (enemy.size.width / 2) {
enemy.position.x = frame.maxX - (enemy.size.width / 2)
}
else if enemy.position.x < frame.minX + (enemy.size.width / 2) {
enemy.position.x = frame.minX + (enemy.size.width / 2)
}
}
}
override func update(_ currentTime: TimeInterval) {
launchEnemy()
}
override func didFinishUpdate() {
keepEnemyInBounds()
}
CONTACT LISTENER CODE:
func didBegin(_ contact: SKPhysicsContact) {
_ = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if (contact.bodyA.categoryBitMask == BodyType.enemy.rawValue && contact.bodyB.categoryBitMask == BodyType.bullet.rawValue) {
if let missile = contact.bodyA.node! as? EnemyClass {
enemyMissileAndBullet(missile)
}
contact.bodyB.node?.name = "removeNode"
} else if ( contact.bodyA.categoryBitMask == BodyType.bullet.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue) {
if let missile = contact.bodyB.node! as? EnemyClass {
enemyMissileAndBullet(missile)
}
contact.bodyA.node?.name = "removeNode"
}
if ( contact.bodyA.categoryBitMask == BodyType.player.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue) {
createExplosion(contact.bodyB.node!.position , image:"explosion2")
contact.bodyB.node?.name = "removeNode"
updateScore(1)
subtractHealth()
playSound("shipExplosion")
} else if (contact.bodyA.categoryBitMask == BodyType.enemy.rawValue && contact.bodyB.categoryBitMask == BodyType.player.rawValue) {
createExplosion(contact.bodyA.node!.position , image:"explosion2")
contact.bodyA.node?.name = "removeNode"
updateScore(1)
subtractHealth()
playSound("shipExplosion")
}
}
EDIT:
Contact listener cleaned up: Requires player < missle < enemy
func didBegin(_ contact: SKPhysicsContact) {
let bodyA = contact.bodyA.categoryBitMask >= contact.bodyA.categoryBitMask ? contact.bodyA : contact.bodyB
let bodyB = contact.bodyA.categoryBitMask < contact.bodyA.categoryBitMask ? contact.bodyA : contact.bodyB
if contact.bodyA.categoryBitMask == BodyType.bullet.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue {
if let missile = contact.bodyA.node! as? EnemyClass {
enemyMissileAndBullet(missile)
}
contact.bodyB.node?.name = "removeNode"
}
else if contact.bodyA.categoryBitMask == BodyType.player.rawValue && contact.bodyB.categoryBitMask == BodyType.enemy.rawValue {
createExplosion(bodyB.node!.position , image:"explosion2")
bodyB.node?.name = "removeNode"
updateScore(1)
subtractHealth()
playSound("shipExplosion")
}
}

contacts not recognized when body is changed from circle to rectangle

With help from here I have made a circle body traverse a given path. I have some bodies at some of the path points and have logged contact in didBeginContact. When the body gets in contact with a specific body the circle body is changed to a rectangle. This rectangular body is suppose to traverse the same path as the original circle body but it doesn't reach the path points as the contact is not logged. I tried changing radiusPoint to the width or height of the rectangle also but that didn't work. Also the rectangle body is bigger than the circle body. How can I get the rectangle to traverse the points with the contact recognised? Please see code below.
Code related to path traversal:
let repeats: Bool = true //Whether to repeat the path.
var pathIndex = 0 //The index of the current point to travel.
var pointRadius: CGFloat = SKTexture(imageNamed: "circle").size().width //How close the node must be to reach the destination point.
let travelSpeed: CGFloat = 250 //Speed the node will travel at.
let rate: CGFloat = 0.9 //Motion smoothing. 0.5
circlePath = [
CGPoint(x:screenSize.width , y: screenSize.height/3),
CGPoint(x: screenSize.width/2, y: platform.sprite.frame.height),
CGPoint(x: 0.0, y: screenSize.height/3),
CGPoint(x: CGFloat(pos1) + screenSize.width/20, y: upperSpearPosHeight)]
final func didReachPoint() {
//reached point!
pathIndex++
if pathIndex >= ballPath.count && repeats {
pathIndex = 0
}
}
func updatePath() {
if pathIndex >= 0 && pathIndex < circlePath.count {
let destination = circlePath[pathIndex]
//currentPosition = destination
let displacement = CGVector(dx: destination.x-circle!.sprite.position.x, dy: destination.y-circle!.sprite.position.y)
let radius = sqrt(displacement.dx*displacement.dx+displacement.dy*displacement.dy)
let normal = CGVector(dx: displacement.dx/radius, dy: displacement.dy/radius)
let impulse = CGVector(dx: normal.dx*travelSpeed, dy: normal.dy*travelSpeed)
let relativeVelocity = CGVector(dx:impulse.dx-circle!.sprite.physicsBody!.velocity.dx, dy:impulse.dy-circle!.sprite.physicsBody!.velocity.dy);
circle!.sprite.physicsBody!.velocity=CGVectorMake(circle!.sprite.physicsBody!.velocity.dx+relativeVelocity.dx*rate, circle!.sprite.physicsBody!.velocity.dy+relativeVelocity.dy*rate);
if radius < pointRadius {
didReachPoint()
}
}
}
Contact code:
func didBeginContact(contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody
var secondBody : SKPhysicsBody
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask {
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else {
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if firstBody.categoryBitMask == circleCategory && secondBody.categoryBitMask == bonusCategory {
let img = SKTexture(imageNamed: "rectangular")
(firstBody.node! as? SKSpriteNode)?.size = img.size()
firstBody.node!.physicsBody = SKPhysicsBody(texture: img, size: img.size())
firstBody.node!.physicsBody?.allowsRotation = false
changeCircleAction = SKAction.setTexture(img)
firstBody.node!.runAction(changeCircleAction)
}
if firstBody.categoryBitMask == circleCategory && secondBody.categoryBitMask == platformCategory {
print("touched platform")
}
if firstBody.categoryBitMask == circleCategory && secondBody.categoryBitMask == smallStarCategory {
removeStar(secondBody.node!)
}
It seems like when you change the firstBody to a rectangle (in the didBeginContact method), you don't set the bit mask. From what you described, it appears you want to set it to circleCategory as such:
firstBody.node!.physicsBody?.categoryBitMask = circleCategory
I would put this right below the firstBody.node!.physicsBody?.allowsRotation = false.

Can't modify contact.bodyA in didBeginContact

I'm new to SpriteKit and Swift. Spent too much time trying to apply position/velocity changes to a body in didBeginContact, only to read in the Apple docs that contact is read only. What is a good way to go about modifying bodies involved in a collision?
struct PhysicsCategory
{
static let None: UInt32 = 0
static let All: UInt32 = UInt32.max
static let LeftWall: UInt32 = 0b1
static let Food: UInt32 = 0b10
}
override func didMoveToView(view: SKView)
{
for(var i = 0; i < 10; ++i)
{
var temp = SKSpriteNode(color: UIColor(red: 0.0, green: 0.7, blue: 0.0, alpha: 1.0), size: CGSizeMake(size, size))
temp.position.x = self.scene!.size.width/2.0
temp.position.y = self.scene!.size.height/2.0
temp.physicsBody = SKPhysicsBody(rectangleOfSize: temp.size)
temp.physicsBody!.categoryBitMask = PhysicsCategory.Food
temp.physicsBody!.contactTestBitMask = PhysicsCategory.LeftWall
temp.physicsBody!.collisionBitMask = PhysicsCategory.None
temp.physicsBody!.velocity.dx = self.randomBetweenNumbers(0.0, secondNum: 0.5) - 0.25
temp.physicsBody!.velocity.dy = self.randomBetweenNumbers(0.0, secondNum: 0.5) - 0.25
self.addChild(temp)
}
}
func didBeginContact(contact: SKPhysicsContact)
{
if (contact.bodyA.categoryBitMask & PhysicsCategory.Food != 0) && (contact.bodyB.categoryBitMask & PhysicsCategory.LeftWall != 0)
{
contact.bodyA!.velocity.dx = 0.0
println("\(contact.bodyA.velocity.dx)") //isn't 0.0
}
}
out of the class
let firstObjectCategory:UInt32 = 0x1 << 0
let secondObjectCategory:UInt32 = 0x1 << 1
var marker:Int= 0
In you DidMoveToView
node1.physicsBody?.categoryBitMask = firstObjectCategory
node1.physicsBody?.contactTestBitMask = secondObjectCategory
if marker=1
{
node1.velocity.dx = 0.0
}else{
node1.velocity.dx = what you want
}
node2.physicsBody?.categoryBitMask = secondObjectCategory
node2.physicsBody?.contactTestBitMask = firstObjectCategory
test the contact between two physic bodies :
func didBeginContact(contact: SKPhysicsContact!)
{
var firstBody: SKPhysicsBody
var secondBody: SKPhysicsBody
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA
secondBody = contact.bodyB
}
else
{
firstBody = contact.bodyB
secondBody = contact.bodyA
}
if ((firstBody.categoryBitMask & firstObjectCategory) != 0 && (secondBody.categoryBitMask & secondObjectCategory) != 0)
{
marker=1
}
}
I hope it will help you