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")
}
}
Related
I'm new with Xcode and I'm trying to make a flappy bird game but I'm having troubles with the score and collision code, the game runs but it is infinite because the character never dies when touch the walls or ground and when I take the coins the score level don't increase, someone can help me with this please?
func createScene(){
self.physicsWorld.contactDelegate = self
for i in 0..<2 {
let background = SKSpriteNode(imageNamed: "Background")
background.anchorPoint = CGPoint.zero
background.position = CGPoint(x: CGFloat(i) * self.frame.width,y: 0)
background.name = "background"
background.size = (self.view?.bounds.size)!
self.addChild(background)
}
scoreLbl.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + self.frame.height / 2.5)
scoreLbl.text = "\(score)"
scoreLbl.zPosition = 6
scoreLbl.fontSize = 60
self.addChild(scoreLbl)
Ground = SKSpriteNode(imageNamed: "Ground")
Ground.setScale(0.5)
Ground.position = CGPoint(x: self.frame.width / 2, y: 0 + Ground.frame.height / 2)
Ground.physicsBody = SKPhysicsBody(rectangleOf: Ground.size)
Ground.physicsBody?.categoryBitMask = PhysicsCatagory.Ground
Ground.physicsBody?.collisionBitMask = PhysicsCatagory.Ghost
Ground.physicsBody?.contactTestBitMask = PhysicsCatagory.Ghost
Ground.physicsBody?.affectedByGravity = false
Ground.physicsBody?.isDynamic = false
Ground.zPosition = 3
self.addChild(Ground)
Ghost = SKSpriteNode(imageNamed: "Ghost")
Ghost.size = CGSize(width: 60, height: 70)
Ghost.position = CGPoint(x: self.frame.width / 2 - Ghost.frame.width, y: self.frame.height / 2)
Ghost.physicsBody = SKPhysicsBody(circleOfRadius: Ghost.frame.height / 2)
Ghost.physicsBody?.categoryBitMask = PhysicsCatagory.Ghost
Ghost.physicsBody?.collisionBitMask = PhysicsCatagory.Ground | PhysicsCatagory.Wall
Ghost.physicsBody?.contactTestBitMask = PhysicsCatagory.Ground | PhysicsCatagory.Wall | PhysicsCatagory.Score
Ghost.physicsBody?.affectedByGravity = false
Ghost.physicsBody?.isDynamic = true
Ghost.zPosition = 2
self.addChild(Ghost)
}
override func didMove(to view: SKView) {
/* Setup your scene here */
createScene()
}
func createBTN(){
restartBTN = SKSpriteNode(imageNamed: "RestartBtn")
restartBTN.size = CGSize(width: 200,height: 100)
restartBTN.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
restartBTN.zPosition = 6
restartBTN.setScale(0)
self.addChild(restartBTN)
restartBTN.run(SKAction.scale(to: 1.0, duration: 0.3))
}
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA
let secondBody = contact.bodyB
if firstBody.categoryBitMask == PhysicsCatagory.Score && secondBody.categoryBitMask == PhysicsCatagory.Ghost{
score+=1
scoreLbl.text = "score"
firstBody.node?.removeFromParent()
}
else if firstBody.categoryBitMask == PhysicsCatagory.Ghost && secondBody.categoryBitMask == PhysicsCatagory.Score {
score+=1
scoreLbl.text = "score"
secondBody.node?.removeFromParent()
}
else if firstBody.categoryBitMask == PhysicsCatagory.Ghost && secondBody.categoryBitMask == PhysicsCatagory.Wall || firstBody.categoryBitMask == PhysicsCatagory.Wall && secondBody.categoryBitMask == PhysicsCatagory.Ghost{
enumerateChildNodes(withName: "wallPair", using: ({
(node, error) in
node.speed = 0
self.removeAllActions()
}))
if died == false{
died = true
createBTN()
}
}
else if firstBody.categoryBitMask == PhysicsCatagory.Ghost && secondBody.categoryBitMask == PhysicsCatagory.Ground || firstBody.categoryBitMask == PhysicsCatagory.Wall && secondBody.categoryBitMask == PhysicsCatagory.Ghost{
enumerateChildNodes(withName: "wallPair", using: ({
(node, error) in
node.speed = 0
self.removeAllActions()
}))
if died == false{
died = true
createBTN()
}
}
}
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])
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)
}
}
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?
I would like to be able to identify which exact target sprite node is hit when the user flings another sprite at a group of target sprites. Here is how I set up my sprites under the didMoveToView function (only including the relevant lines of code here)
let flingerTexture = SKTexture(imageNamed: "flinger")
flingerNode.position = CGPoint(x: 768, y: 440)
flingerNode.physicsBody = SKPhysicsBody(texture: flingerTexture, size: flingerNode.size)
flingerNode.physicsBody?.dynamic = true
flingerNode.physicsBody!.categoryBitMask = PhysicsCategory.Flinger
flingerNode.physicsBody!.collisionBitMask = PhysicsCategory.Edge | PhysicsCategory.Bubble | PhysicsCategory.Ball
flingerNode.physicsBody?.contactTestBitMask = PhysicsCategory.Ball
let rotationConstraint = SKConstraint.zRotation(SKRange(lowerLimit: -π/4, upperLimit: π/4))
flingerNode.constraints = [rotationConstraint]
addChild(flingerNode)
// -------------Setup targets---------------
let range: Range<Int> = 1...10
for numbers in range {
let ballNode: BallNode = BallNode(imageNamed: "\(numbers)a")
let positionX = CGFloat.random(min: size.width / 6, max: size.width * 5/6)
let positionY = CGFloat.random(min: size.height * 4/9, max: size.height * 8/9)
ballNode.position = CGPoint(x: positionX, y: positionY)
ballNode.name = "Ball"
ballNode.ballIndex = Int(numbers)
index = ballNode.ballIndex
ballNode.ballHit = false
addChild(ballNode)
ballNode.physicsBody = SKPhysicsBody(circleOfRadius: 100)
ballNode.physicsBody!.affectedByGravity = false
ballNode.physicsBody?.dynamic = true
ballNode.physicsBody!.restitution = 0.5
ballNode.physicsBody!.friction = 0.0
ballNode.physicsBody!.categoryBitMask = PhysicsCategory.Ball
ballNode.physicsBody!.collisionBitMask = PhysicsCategory.Ball | PhysicsCategory.Bubble | PhysicsCategory.Edge | PhysicsCategory.Flinger | PhysicsCategory.Wall
ballNode.physicsBody?.contactTestBitMask = PhysicsCategory.Flinger
ballNode.userData = NSMutableDictionary()
ballArray.append(ballNode.ballIndex)
}
I am able to detect the collision, but am unable to retrieve the additional userData that would identify which exact ballNode was struck. When I tried the following code, it only returns an output of "nil".
func didBeginContact(contact: SKPhysicsContact!) {
let collision = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
if collision == PhysicsCategory.Flinger | PhysicsCategory.Ball {
println(ballNode.userData)
}
}
I am assuming that PhysicsCategory.Flinger is less than PhysicsCategory.Ball. Then in didContactBegan you can use this code.
func didBeginContact(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 == PhysicsCategory.Flinger &&
body2.categoryBitMask == PhysicsCategory.Ball {
if let ballNode = body2.node {
println(ballNode.userData)
}
}
}
The conditions have to be reversed if PhysicsCategory.Flinger is greater than PhysicsCategory.Ball.
if body1.categoryBitMask == PhysicsCategory.Ball &&
body2.categoryBitMask == PhysicsCategory.Flinger {
if let ballNode = body1.node {
println(ballNode.userData)
}
}
Thanks so much for pointing me in the right direction #rakeshbs! Your didBeginContact method works - the problem was I wasn't adding the userData correctly. I resolved this by adding the following line to my didMoveToView function:
ballNode.userData = ["ballNumber" : ballNode.ballIndex]