I am trying to make a ball bounce within a circle so it cant get out of the circumference of the circle. Now the ball bounce in self.frame. How can I make the ball bounce within the circle?
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
sceneBody.friction = 0
self.physicsBody = sceneBody
var ball = SKShapeNode(circleOfRadius: 9)
ball.fillColor = SKColor.whiteColor()
ball.position = view.center
ball.physicsBody = SKPhysicsBody(circleOfRadius: 9)
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.restitution = 1
ball.physicsBody?.linearDamping = 0
ball.zPosition = 3
self.addChild(ball)
circulo = SKSpriteNode(imageNamed: "circuloFondo2")
//Circle that I want the ball bounce within
circulo.size = CGSize(width:view.frame.size.width - padding , height: view.frame.size.width - padding)
circulo.color = UIColor(red: 0.15, green: 0.15, blue: 0.15, alpha: 1)
circulo.colorBlendFactor = 1
circulo.alpha = 0.35
circulo.position = view.center
self.addChild(circulo)
circulo.zPosition = 1
I've prepared a simple test project to explain how to do it:
import UIKit
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
enum CollisionTypes: UInt32 {
case Circle = 1
case Ball = 2
//case Enemy1 = 4
//case Enemy2 = 8
//case Enemy3 = 16
//case EnemyBoss = 32
}
var circulo: SKSpriteNode!
var padding: CGFloat = 40.0
var center: CGPoint!
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0, -6)
self.physicsWorld.contactDelegate = self
self.center = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
// Prepare circle
circulo = SKSpriteNode(imageNamed: "circuloFondo2.png")
//Circle that I want the ball bounce within
circulo.size = CGSize(width:view.frame.size.width - padding , height: view.frame.size.width - padding)
let circlePath = UIBezierPath(arcCenter: self.center, radius: (view.frame.size.width - padding*2)/2, startAngle: CGFloat(0), endAngle:CGFloat(M_PI * 2), clockwise: true)
circulo.color = UIColor(red: 0.15, green: 0.15, blue: 0.15, alpha: 1)
circulo.colorBlendFactor = 1
circulo.alpha = 1
circulo.position = self.center
circulo.zPosition = 1
// Set physics
let circuloBody = SKPhysicsBody.init(edgeLoopFromPath: circlePath.CGPath)
self.physicsBody = circuloBody
self.physicsBody!.affectedByGravity = false
self.physicsBody!.usesPreciseCollisionDetection = true
self.physicsBody!.dynamic = true
self.physicsBody!.mass = 0
self.physicsBody!.friction = 0
self.physicsBody!.linearDamping = 0
self.physicsBody!.angularDamping = 0
self.physicsBody!.restitution = 1
self.physicsBody!.categoryBitMask = CollisionTypes.Circle.rawValue
self.physicsBody!.contactTestBitMask = CollisionTypes.Ball.rawValue
//self.circulo.physicsBody!.collisionBitMask = 0
self.addChild(circulo)
//Prepare ball
let ball = SKShapeNode(circleOfRadius: 9)
ball.fillColor = SKColor.whiteColor()
ball.position = self.center
ball.zPosition = 1
// Set physics
ball.physicsBody = SKPhysicsBody(circleOfRadius: 9)
ball.physicsBody!.affectedByGravity = true
ball.physicsBody!.restitution = 0.8
ball.physicsBody!.linearDamping = 0
ball.physicsBody!.friction = 0.3
ball.physicsBody?.dynamic = true
ball.physicsBody!.mass = 0.5
ball.physicsBody!.allowsRotation = true
ball.physicsBody!.categoryBitMask = CollisionTypes.Ball.rawValue
ball.physicsBody!.contactTestBitMask = CollisionTypes.Circle.rawValue
//ball.physicsBody!.collisionBitMask = 0
self.addChild(ball)
}
func didBeginContact(contact: SKPhysicsContact) {
// elements
if (contact.bodyA.categoryBitMask == CollisionTypes.Circle.rawValue &&
contact.bodyB.categoryBitMask == CollisionTypes.Ball.rawValue) {
print("contact between circle and ball")
}
}
The circuloFondo2 image:
A demo about the project:
In this answer you can see all the code to do it, but if you want you can download the project here
Related
I am making a little FlappyBird clone and have got everything working as it should until I changed the physics body of the bird to be exact to the texture. Now what it does is when it flies through the gap in the pipes it counts 30 points instead of just 1 point.
This only happens when I use the texture exact physics body which I need because the bird isn't round nor rectangular.
How would I go about making the collision so it only collides once with each gap node. I have tried setting the categoryBitBask to 0 after the contact but then all the gaps after don't add to the point count anymore at all.
Here is the full game code:
var score = 0
class GameScene: SKScene, SKPhysicsContactDelegate {
var bird = SKSpriteNode()
var bg = SKSpriteNode()
var ground = SKSpriteNode()
var scoreLabel = SKLabelNode(fontNamed: "Candice")
var gameOverLabel = SKLabelNode(fontNamed: "Candice")
var countbg = SKSpriteNode()
var timer = Timer()
enum ColliderType: UInt32 {
case Bird = 1
case Object = 2
case Gap = 4
}
var gameOver = false
let swooshSound = SKAction.playSoundFileNamed("sfx_swooshing.wav", waitForCompletion: false)
let pointSound = SKAction.playSoundFileNamed("sfx_point.wav", waitForCompletion: false)
let hitSound = SKAction.playSoundFileNamed("sfx_hit.wav", waitForCompletion: false)
#objc func makePipes() {
let movePipes = SKAction.move(by: CGVector(dx: -2 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width / 150))
let removePipes = SKAction.removeFromParent()
let moveAndRemovePipes = SKAction.sequence([movePipes, removePipes])
let gapHeight = bird.size.height * 2.8
let movementAmount = arc4random() % UInt32(self.frame.height) / 2
let pipeOffset = CGFloat(movementAmount) - self.frame.height / 4
let pipeTexture = SKTexture(imageNamed: "pipe1.png")
let pipe1 = SKSpriteNode(texture: pipeTexture)
pipe1.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeTexture.size().height / 2 + gapHeight / 2 + pipeOffset)
pipe1.zPosition = 2
pipe1.run(moveAndRemovePipes)
pipe1.physicsBody = SKPhysicsBody(rectangleOf: pipeTexture.size())
pipe1.physicsBody!.isDynamic = false
pipe1.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe1.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(pipe1)
let pipe2Texture = SKTexture(imageNamed: "pipe2.png")
let pipe2 = SKSpriteNode(texture: pipe2Texture)
pipe2.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY - pipeTexture.size().height / 2 - gapHeight / 2 + pipeOffset)
pipe2.zPosition = 2
pipe2.run(moveAndRemovePipes)
pipe2.physicsBody = SKPhysicsBody(rectangleOf: pipe2Texture.size())
pipe2.physicsBody!.isDynamic = false
pipe2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
pipe2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(pipe2)
let gap = SKNode()
gap.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + pipeOffset)
gap.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 1, height: gapHeight))
gap.physicsBody!.isDynamic = false
gap.run(moveAndRemovePipes)
gap.physicsBody!.contactTestBitMask = ColliderType.Bird.rawValue
gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue
self.addChild(gap)
}
func didBegin(_ contact: SKPhysicsContact) {
if gameOver == false {
if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {
score += 1
scoreLabel.text = String(format: "%05d", score)
run(pointSound)
} else {
self.speed = 0
run(hitSound)
gameOver = true
timer.invalidate()
bird.removeFromParent()
let changeSceneAction = SKAction.run(changeScene)
self.run(changeSceneAction)
}
}
}
//MARK: Change to Game Over Scene
func changeScene(){
let sceneToMoveTo = GameOverScene(size: self.size)
sceneToMoveTo.scaleMode = self.scaleMode
let myTransition = SKTransition.fade(withDuration: 0.5)
self.view!.presentScene(sceneToMoveTo, transition: myTransition)
}
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
setupGame()
}
func setupGame() {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.makePipes), userInfo: nil, repeats: true)
let groundTexture = SKTexture(imageNamed: "ground.png")
let moveGroundAnimation = SKAction.move(by: CGVector(dx: -groundTexture.size().width, dy: 0), duration: 7)
let shiftGroundAnimation = SKAction.move(by: CGVector(dx: groundTexture.size().width, dy: 0), duration: 0)
let moveGroundForever = SKAction.repeatForever(SKAction.sequence([moveGroundAnimation, shiftGroundAnimation]))
var i: CGFloat = 0
while i < 3 {
ground = SKSpriteNode(texture: groundTexture)
ground.position = CGPoint(x: self.size.width * i, y: self.size.height / 7.65)
ground.zPosition = 3
ground.run(moveGroundForever)
self.addChild(ground)
i += 1
}
let bottom = SKNode()
bottom.position = CGPoint(x: self.frame.midX, y: self.size.height / 7)
bottom.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.width, height: 1))
bottom.physicsBody!.isDynamic = false
bottom.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bottom.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
bottom.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(bottom)
let bgTexture = SKTexture(imageNamed: "bg.png")
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
bg.size = self.frame.size
bg.zPosition = 1
self.addChild(bg)
let birdTexture = SKTexture(imageNamed: "flappy1.png")
let bird2Texture = SKTexture(imageNamed: "flappy2.png")
let bird3Texture = SKTexture(imageNamed: "flappy3.png")
let bird4Texture = SKTexture(imageNamed: "flappy4.png")
let bird5Texture = SKTexture(imageNamed: "flappy5.png")
let bird6Texture = SKTexture(imageNamed: "flappy6.png")
let animation = SKAction.animate(with: [birdTexture, bird2Texture, bird3Texture, bird4Texture, bird5Texture, bird6Texture], timePerFrame: 0.1)
let makeBirdFlap = SKAction.repeatForever(animation)
bird = SKSpriteNode(texture: birdTexture)
bird.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
bird.setScale(1)
bird.zPosition = 6
bird.run(makeBirdFlap)
self.addChild(bird)
bird.physicsBody = SKPhysicsBody.init(circleOfRadius: birdTexture.size().height / 2)
//bird.physicsBody = SKPhysicsBody(texture: birdTexture, size: birdTexture.size())
bird.physicsBody!.isDynamic = false
bird.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
bird.physicsBody!.categoryBitMask = ColliderType.Bird.rawValue
bird.physicsBody!.collisionBitMask = ColliderType.Bird.rawValue
let countbg = SKSpriteNode(imageNamed: "count_bg.png")
countbg.position = CGPoint(x: self.size.width / 4.8, y: self.size.height * 0.94)
countbg.setScale(0.8)
countbg.zPosition = 4
addChild(countbg)
scoreLabel.fontSize = 80
scoreLabel.text = String(format: "%05d", score)
scoreLabel.fontColor = SKColor(red: 218/255, green: 115/255, blue: 76/255, alpha: 1)
scoreLabel.position = CGPoint(x: self.size.width / 4, y: self.size.height * 0.94)
scoreLabel.zPosition = 5
addChild(scoreLabel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if gameOver == false {
bird.physicsBody!.isDynamic = true
bird.physicsBody!.velocity = CGVector(dx: 0, dy: 0)
bird.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 280))
//run(swooshSound)
} else {
gameOver = false
score = 0
self.speed = 1
self.removeAllChildren()
setupGame()
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
If you would be using RxSwift, you would be able to easily get rid of those extra events easily by using debounce() or throttle() or distinctUntilChanged(). If you are willing to try this approach, give RxSpriteKit framework a go. Otherwise, store a timestamp of the last contact and ignore the following contacts until some time period elapses.
I am quite new to Swift and the Sprite Kit!
I tried to build a Pong game. Everything works well but the ball on which I am applying an impulse slows down really quickly!
I already turned off gravity, friction and damping and set the restitution of the ball and the scene to 1.
Here is my Code:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
let player = SKShapeNode(rectOf: CGSize(width: 200, height: 30))
let enemy = SKShapeNode(rectOf: CGSize(width: 200, height: 30))
let ball = SKShapeNode(circleOfRadius: 10)
override func didMove(to view: SKView) {
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.restitution = 1
border.friction = 0
border.angularDamping = 0
border.linearDamping = 0
self.physicsBody = border
scene?.physicsWorld.gravity = CGVector(dx: 0, dy: 0)
player.position = CGPoint(x: 0, y: -(scene?.size.height)! * 0.35)
player.fillColor = UIColor.white
player.strokeColor = UIColor.clear
self.addChild(player)
player.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 200, height: 30))
player.physicsBody?.friction = 0
player.physicsBody?.isDynamic = false
player.physicsBody?.angularDamping = 0
player.physicsBody?.linearDamping = 0
player.physicsBody?.restitution = 1
player.physicsBody?.affectedByGravity = false
enemy.position = CGPoint(x: 0, y: (scene?.size.height)! * 0.35)
enemy.fillColor = UIColor.white
enemy.strokeColor = UIColor.clear
self.addChild(enemy)
enemy.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 200, height: 30))
enemy.physicsBody?.friction = 0
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.linearDamping = 0
enemy.physicsBody?.angularDamping = 0
enemy.physicsBody?.isDynamic = false
ball.position = CGPoint(x: 0, y: 0)
ball.fillColor = UIColor.white
ball.strokeColor = UIColor.clear
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.affectedByGravity = true
ball.physicsBody = SKPhysicsBody(circleOfRadius: 10)
ball.physicsBody?.isDynamic = true
self.addChild(ball)
ball.physicsBody?.applyImpulse(CGVector(dx: -30, dy: -30))
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let pos = touch.location(in: self)
player.position.x = pos.x
}
}
override func update(_ currentTime: TimeInterval) {
if ball.position.y > 0{
enemy.position.x = ball.position.x
}
}
}
Thank you already!
Be sure to always create your physics body before you change its properties
ball.physicsBody?.angularDamping = 0
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.allowsRotation = false
ball.physicsBody?.affectedByGravity = true
ball.physicsBody = SKPhysicsBody(circleOfRadius: 10) <----Needs to be first
ball.physicsBody?.isDynamic = true
self.addChild(ball)
ball.physicsBody?.applyImpulse(CGVector(dx: -30, dy: -30))
How do you get contact between nodes from different hierarchy in the node tree, say a node in the scene (ball) and a child node (wall) of another node.
I have tried to build a plate with 4 walls which are child node of the plate. then I added a ball to the plate node (I also tried to let the ball be in the scene) and let it move. after defining the physics body, still I can't get a contact.
Here is my code:
class GameScene: SKScene {
let plate = SKSpriteNode ()
let wall1 = SKSpriteNode ()
let wall2 = SKSpriteNode ()
let wall3 = SKSpriteNode ()
let wall4 = SKSpriteNode ()
var ball = SKShapeNode ()
override func didMove(to view: SKView) {
self.backgroundColor = .white
plate.size = CGSize (width: self.size.width * 0.6 , height: self.size.width * 0.6)
plate.position = CGPoint (x: self.size.width / 2 , y: self.size.height / 2 )
plate.color = .orange
plate.zPosition = 0
self.addChild(plate)
plate.physicsBody = SKPhysicsBody (edgeLoopFrom: plate.frame)
plate.physicsBody?.isDynamic = false
plate.physicsBody?.friction = 0
plate.physicsBody?.restitution = 1
plate.physicsBody?.affectedByGravity = false
plate.physicsBody?.categoryBitMask = 1
plate.physicsBody?.contactTestBitMask = 0
plate.physicsBody?.collisionBitMask = 0
wall1.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall1.position = CGPoint (x: plate.size.width / 2, y: 0 )
wall1.color = .gray
wall1.zPosition = 2
plate.addChild(wall1)
wall1.physicsBody = SKPhysicsBody (edgeLoopFrom: wall1.frame)
wall2.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall2.position = CGPoint (x: 0, y: plate.size.height / 2 )
wall2.zRotation = plate.zRotation + .pi/2
wall2.color = .yellow
wall2.zPosition = 2
plate.addChild(wall2)
wall2.physicsBody = SKPhysicsBody (edgeLoopFrom: wall2.frame)
wall3.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall3.position = CGPoint (x: -plate.size.width / 2, y: 0 )
wall3.color = .cyan
wall3.zPosition = 2
plate.addChild(wall3)
wall3.physicsBody = SKPhysicsBody (edgeLoopFrom: wall3.frame)
wall4.size = CGSize (width: plate.size.width * 0.1, height: plate.size.height)
wall4.position = CGPoint (x: 0, y: -plate.size.height / 2 )
wall4.zRotation = plate.zRotation + .pi/2
wall4.color = .magenta
wall4.zPosition = 2
plate.addChild(wall4)
wall4.physicsBody = SKPhysicsBody (edgeLoopFrom: wall4.frame)
wall4.physicsBody?.isDynamic = false
wall4.physicsBody?.friction = 0
wall4.physicsBody?.restitution = 1
wall4.physicsBody?.affectedByGravity = false
wall4.physicsBody?.categoryBitMask = 1
wall4.physicsBody?.contactTestBitMask = 0
wall4.physicsBody?.collisionBitMask = 0
ball = SKShapeNode (circleOfRadius: 10)
ball.strokeColor = .black
ball.lineWidth = 2
ball.fillColor = .blue
ball.position = CGPoint (x: self.size.width/2, y: self.size.height/2)
ball.zPosition = 2
plate.addChild(ball)
ball.physicsBody = SKPhysicsBody (circleOfRadius: 10)
ball.physicsBody?.isDynamic = true
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.friction = 0
ball.physicsBody?.restitution = 1
ball.physicsBody?.categoryBitMask = 0
ball.physicsBody?.contactTestBitMask = 1
ball.physicsBody?.collisionBitMask = 1
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
plate.zRotation -= .pi/36
}}
I can't even have contact between my ball and the borders of the plate itself.
Thanks
I have a 40x40 rectangle node, and I want to detect when the bottom touches a platform.
I tried this
let feet = SKPhysicsBody(rectangleOfSize: CGSize(width: hero.frame.size.width, height: 1), center: CGPoint(x: 0, y: -(hero.frame.size.height/2 - 0.5)))
then set the categoryBitMask, collisionBitMask, contactTestBitMaskand added it to the hero
hero.physicsBody = SKPhysicsBody(bodies: [feet])
But in didBeginContact the println() doesn't print.
I need a body for the bottom of the rectangle and one for the top, because if hero hits a platform from below the collision should push him down.
Update
Here is how I set the bit masks
let heroFeetCategory: UInt32 = 1 << 0
let edgeCategory: UInt32 = 1 << 1
let groundCategory: UInt32 = 1 << 2
let feet = SKPhysicsBody(rectangleOfSize: CGSize(width: hero.frame.size.width, height: 10), center: CGPoint(x: 0, y: -(hero.frame.size.height/2 - 5)))
feet.collisionBitMask = edgeCategory | groundCategory
feet.contactTestBitMask = groundCategory
feet.categoryBitMask = heroFeetCategory
hero.physicsBody = SKPhysicsBody(bodies: [feet])
hero.physicsBody?.usesPreciseCollisionDetection = true
hero.physicsBody?.velocity = CGVectorMake(0, 0)
hero.physicsBody?.restitution = 0.0
hero.physicsBody?.friction = 0.0
hero.physicsBody?.angularDamping = 0.0
hero.physicsBody?.linearDamping = 1.0
hero.physicsBody?.allowsRotation = false
hero.physicsBody?.mass = 0.0641777738928795
world.addChild(hero)
and for the ground
let ground = SKSpriteNode(color: UIColor.whiteColor(), size: CGSizeMake(38, 38))
ground.name = "groundName"
ground.position = CGPoint(x: 0, y: -(self.frame.size.height/2 - ground.frame.size.height/2))
ground.physicsBody = SKPhysicsBody(rectangleOfSize: ground.size)
ground.physicsBody?.collisionBitMask = edgeCategory | heroFeetCategory
ground.physicsBody?.contactTestBitMask = heroFeetCategory
ground.physicsBody?.categoryBitMask = groundCategory
world.addChild(ground)
And how I detect if they touch
func didBeginContact(contact: SKPhysicsContact) {
var notTheHero: SKPhysicsBody!;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask) {
notTheHero = contact.bodyB;
} else {
notTheHero = contact.bodyA;
}
println(notTheHero.node) // print "heroName"
if (notTheHero.categoryBitMask == groundCategory) {
println("touch began?"); // is never called
}
}
collision is fine, but when you print the node's name through the physics body with notTheHero.node you only access the SKNode, and not the NSString property of its name. Instead, try something like println(notTheHero.node.name);
I am working on game where a ball bounces and hits a platform and then bounces right back. Ideally the ball should bounce to exactly the height that it started at. However, in my game the ball slowly keeps bouncing higher and higher. I've looked at the documentation and tried to change all of the physics properties but nothing seems to work.
Any suggestions?
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let ballCategory:UInt32 = 0x1 << 0;
let circleCategory:UInt32 = 0x1 << 1;
let platformCategory:UInt32 = 0x1 << 2;
var ball = SKShapeNode(circleOfRadius: 20.0)
var circle = SKShapeNode(circleOfRadius: 200.0)
var platform = SKShapeNode(rectOfSize: CGSizeMake(10, 1))
var circleColor = 2
var ballColor = 3
var scoreLabel: SKLabelNode!
var score = 0
override func didMoveToView(view: SKView) {
setUpLabels()
self.physicsWorld.contactDelegate = self
backgroundColor = (UIColor.whiteColor())
ball.fillColor = SKColor.redColor()
ball.strokeColor = SKColor.clearColor()
ball.position = CGPoint(x: self.size.width/2, y: self.size.height/2+60)
ball.physicsBody = SKPhysicsBody(circleOfRadius: 20)
ball.physicsBody?.categoryBitMask = ballCategory
ball.physicsBody?.collisionBitMask = platformCategory
ball.physicsBody?.contactTestBitMask = platformCategory
ball.physicsBody?.dynamic = true
ball.physicsBody?.restitution = 1.0
ball.physicsBody?.affectedByGravity = true
ball.physicsBody?.linearDamping = 0.0
ball.physicsBody?.friction = 0.0
self.addChild(ball)
circle.fillColor = SKColor.clearColor()
circle.strokeColor = SKColor.redColor()
circle.lineWidth = 5.0
circle.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
circle.physicsBody = SKPhysicsBody(edgeLoopFromPath: circle.path)
circle.physicsBody?.categoryBitMask = circleCategory
self.addChild(circle)
platform.fillColor = SKColor.clearColor()
platform.strokeColor = SKColor.clearColor()
platform.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(10, 1))
platform.position = CGPoint(x: self.size.width/2, y: circle.position.y/2)
platform.physicsBody?.categoryBitMask = platformCategory
platform.physicsBody?.dynamic = false
platform.physicsBody?.friction = 0.0
self.addChild(platform)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
circleColor += 1
if circleColor%3 == 1 {
circle.strokeColor = UIColor.redColor()
}
if circleColor%3 == 2 {
circle.strokeColor = UIColor.greenColor()
}
if circleColor%3 == 3 {
circle.strokeColor = UIColor.yellowColor()
}
if circleColor%3 == 4 {
circle.strokeColor = UIColor.greenColor()
}
if circleColor%3 == 5 {
circle.strokeColor = UIColor.blueColor()
}
if circleColor%3 == 0 {
circle.strokeColor = UIColor.yellowColor()
}
}
func didBeginContact(contact: SKPhysicsContact) {
if ball.fillColor == circle.strokeColor {
score += 1
scoreLabel.text = String("Score \(score)")
}
if ball.fillColor != circle.strokeColor {
let transition = SKTransition.revealWithDirection(SKTransitionDirection.Down, duration: 1.0)
let scene = SecondScene(size: self.scene!.size)
scene.scaleMode = SKSceneScaleMode.AspectFill
self.scene!.view!.presentScene(scene, transition: transition)
}
ballColor = Int(arc4random_uniform(10))
if ballColor%3 == 1 {
ball.fillColor = UIColor.redColor()
}
if ballColor%3 == 2 {
ball.fillColor = UIColor.greenColor()
}
if ballColor%3 == 3 {
ball.fillColor = UIColor.yellowColor()
}
if ballColor%3 == 4 {
ball.fillColor = UIColor.greenColor()
}
if ballColor%3 == 5 {
ball.fillColor = UIColor.blueColor()
}
if ballColor%3 == 0 {
ball.fillColor = UIColor.yellowColor()
}
}
func setUpLabels () {
scoreLabel = SKLabelNode(fontNamed: "Arial")
scoreLabel.position = CGPoint(x: 60, y: self.size.height-30)
scoreLabel.text = String("Score \(score)")
scoreLabel.fontColor = UIColor.blackColor()
scoreLabel.fontSize = 25
self.addChild(scoreLabel)
}
override func update(currentTime: CFTimeInterval) {
}
}
Reduce restitution a little which take control of the bounciness of the physics body. Like this:
ball.physicsBody?.restitution = 0.9
In general numerical differential equation solvers that solve the ode for the next time step will introduce energy into the system, which is why you are seeing what you are seeing. You can mitigate this by introduce a damping force. In the case of SpriteKit this means changing the restitution or linear damping factors.
For more about the drift that occurs see: https://en.wikipedia.org/wiki/Energy_drift or http://www.cs.cmu.edu/~baraff/sigcourse/notesb.pdf.