Use random number (arc4random_uniform) to display random shapes - swift

The purpose for this code is to do random number to display random shapes but the simulator displays them in the same spot which is the bottom left corner of the screen, for some reason the random for the position which is a is not working.
Code:
import UIKit
import SpriteKit
import Darwin
class StartGame: SKScene {
var scoreLabel = SKLabelNode(fontNamed: "cholkDuster")
var square = SKSpriteNode(imageNamed: "square")
var circle = SKSpriteNode(imageNamed: "circle")
var rectangle = SKSpriteNode(imageNamed: "rectangle")
var triangle = SKSpriteNode(imageNamed: "triangle")
let bg = SKSpriteNode(imageNamed: "background.png")
var score = 0
override func didMoveToView(view: SKView) {
//random number for the shapes
var timecreatShapes = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: Selector("creatShapes"), userInfo: nil, repeats: true)
//background image
bg.position = CGPoint(x: CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame))
bg.size.width = self.frame.size.width
bg.size.height = self.frame.size.height
self.addChild(bg)
self.scoreLabel.text = "0"
self.scoreLabel.fontSize = 42
self.scoreLabel.position = CGPoint(x: CGRectGetMidX(self.frame) , y: CGRectGetMidY(self.frame))
self.addChild(scoreLabel)
scoreLabel.zPosition = 2
self.physicsWorld.gravity = CGVectorMake(0, -0.5)
//declaring a square image
square.size = CGSize(width: 150, height: 100)
square.color = SKColor.redColor()
square.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
square.physicsBody?.dynamic = true
square.physicsBody?.allowsRotation = false
//declaring a circle image
circle.size = CGSize(width: 150, height: 100)
circle.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
circle.physicsBody?.dynamic = true
circle.physicsBody?.allowsRotation = false
//declaring a triangle
triangle.size = CGSize(width: 150, height: 100)
triangle.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
triangle.physicsBody?.dynamic = true
triangle.physicsBody?.allowsRotation = false
//declaring rectangle
rectangle.size = CGSize(width: 150, height: 100)
rectangle.physicsBody = SKPhysicsBody(circleOfRadius: square.size.height / 2)
rectangle.physicsBody?.dynamic = true
rectangle.physicsBody?.allowsRotation = false
}
func creatShapes () {
var x = Int ( arc4random_uniform(4) + 1)
var a = CGFloat ( arc4random_uniform(1000) + 1)
switch(x){
case 1:
circle.position = CGPoint (x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"circle"))
case 2:
square.position = CGPoint(x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"square"))
case 3:
rectangle.position = CGPoint(x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"rectangle"))
case 4:
triangle.position = CGPoint(x: a , y: self.frame.size.height)
self.addChild(SKSpriteNode(imageNamed:"triangle"))
default:
break
}
println(x)
println(a)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (self.nodeAtPoint(location) == self.square || self.nodeAtPoint(location) == self.triangle || self.nodeAtPoint(location) == self.circle || self.nodeAtPoint(location) == self.rectangle){
self.score++
}
self.scoreLabel.text = String(self.score)
}
}

Related

SpriteKit - didBegin contact is called 30 times instead of 1 time

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.

How to fix SKPhysicsContactDelegate not being called

My sprites are supposed to contact each other and print to console, however, one goes behind the other and they do not actually touch. Needless to say, nothing is being printed to console.
I've tried using many different "types" of if statements in the function, however, none of them have worked. For example, I've tried using:
if bodyA.categoryBitMask == 1 && bodyB.categoryBitMask == 2 || bodyA.categoryBitMask == 2 && bodyB.categoryBitMask == 1
as well as:
if contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask
Help would be much appreciated! :)
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var e = SKSpriteNode()
var square = SKSpriteNode()
var timer:Timer!
let squareCategory:UInt32 = 0x1 << 1
let eCategory:UInt32 = 0x1 << 2
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
square = SKSpriteNode(imageNamed: "square")
square.size = CGSize(width: 100, height: 100)
square.physicsBody = SKPhysicsBody(rectangleOf: square.size)
square.position = CGPoint(x: 0, y: -590)
square.physicsBody = SKPhysicsBody(rectangleOf: e.size)
square.name = "square"
square.physicsBody?.categoryBitMask = squareCategory
square.physicsBody?.contactTestBitMask = eCategory
square.physicsBody?.usesPreciseCollisionDetection = true
self.addChild(square)
let swipeRight: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector(GameScene.swipedRight(sender:)))
swipeRight.direction = .right
view.addGestureRecognizer(swipeRight)
let swipeLeft: UISwipeGestureRecognizer = UISwipeGestureRecognizer(target: self, action: #selector (GameScene.swipedLeft(sender:)))
swipeLeft.direction = .left
view.addGestureRecognizer(swipeLeft)
timer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(self.adde), userInfo: nil, repeats: true)
}
#objc func swipedRight(sender: UISwipeGestureRecognizer) {
//middle
if square.position == CGPoint(x: 0, y: -590) {
square.position = CGPoint(x: 200, y: -590)
}
//left
if square.position == CGPoint(x: -200, y: -590) {
square.position = CGPoint(x: 0, y: -590)
}
}
#objc func swipedLeft(sender: UISwipeGestureRecognizer) {
//middle
if square.position == CGPoint(x: 0, y: -590) {
square.position = CGPoint(x: -200, y: -590)
}
//right
if square.position == CGPoint(x: 200, y: -590) {
square.position = CGPoint(x: 0, y: -590)
}
}
#objc func adde() {
e = SKSpriteNode(imageNamed: "e")
let ePosition = GKRandomDistribution(lowestValue: -414, highestValue: 414)
let position = CGFloat(ePosition.nextInt())
e.size = CGSize(width: 75, height: 75)
e.position = CGPoint(x: position, y: 640)
e.physicsBody = SKPhysicsBody(rectangleOf: e.size)
e.name = "e"
e.physicsBody?.categoryBitMask = eCategory
e.physicsBody?.contactTestBitMask = squareCategory
e.physicsBody?.isDynamic = true
self.addChild(e)
let animationDuration:TimeInterval = 6
var actionArray = [SKAction]()
actionArray.append(SKAction.move(to: CGPoint(x: position, y: -705), duration: animationDuration))
actionArray.append(SKAction.removeFromParent())
e.run(SKAction.sequence(actionArray))
}
func didBegin(_ contact: SKPhysicsContact) {
let bodyAName = contact.bodyA.node?.name
let bodyBName = contact.bodyB.node?.name
if bodyAName == "square" && bodyBName == "e" || bodyAName == "e" && bodyBName == "square"{
if bodyAName == "square" {
contact.bodyA.node?.removeFromParent()
} else if bodyBName == "e" {
contact.bodyB.node?.removeFromParent()
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
I am supposed to get the message, but the "e" goes behind the "square" and I don't get any message.
There is an error in your code.
Change the following:
square = SKSpriteNode(imageNamed: "square")
square.size = CGSize(width: 100, height: 100)
square.physicsBody = SKPhysicsBody(rectangleOf: square.size)
square.position = CGPoint(x: 0, y: -590)
square.physicsBody = SKPhysicsBody(rectangleOf: e.size)
square.name = "square"
to
square = SKSpriteNode(imageNamed: "square")
square.color = UIColor.red
square.size = CGSize(width: 100, height: 100)
square.physicsBody = SKPhysicsBody(rectangleOf: square.size)
square.position = CGPoint(x: 0, y: horizotaly)
//square.physicsBody = SKPhysicsBody(rectangleOf: e.size)
square.physicsBody?.affectedByGravity = false
square.name = "square"
Your problem is solved.

Looping my code so that the user can continue to play

import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let balls = [
SKSpriteNode(imageNamed: "blueball.png"),
SKSpriteNode(imageNamed: "greenball.png"),
SKSpriteNode(imageNamed: "realredball.png"),
]
let redRectangle = SKSpriteNode(imageNamed: "redrectangle.png")
let blueRectangle = SKSpriteNode(imageNamed: "bluerectangle.png")
let greenRectangle = SKSpriteNode(imageNamed: "greenrectangle.png")
let wall1 = SKSpriteNode(imageNamed: "drop_wall.png")
let wall2 = SKSpriteNode(imageNamed: "drop_wall.png")
let bottom = SKSpriteNode(imageNamed:"drop_bottom.png")
let top = SKSpriteNode(imageNamed:"drop_bottom.png")
let blueBallCategory :UInt32 = 0x1 << 0
let greenBallCategory :UInt32 = 0x1 << 1
let realRedBallCategory :UInt32 = 0x1 << 2
let redRectangleCategory : UInt32 = 0x1 << 3
let blueRectangleCategory : UInt32 = 0x1 << 4
let greenRectangleCategory : UInt32 = 0x1 << 5
override func didMove(to view: SKView) {
spawnBalls()
spawnRectangles()
physicsWorld.contactDelegate = self
moveRectangles()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for ball in balls{
ball.isUserInteractionEnabled = false
}
physics()
}
func didBegin(_ contact: SKPhysicsContact) {
let contactMask = contact.bodyA.categoryBitMask | contact.bodyB.categoryBitMask
switch contactMask {
case blueBallCategory | blueRectangleCategory:
for ball in balls{
ball.removeFromParent()
}
print("Alive! Blue ball has hit blue rectangle.")
case greenBallCategory | greenRectangleCategory:
print("Alive! Green ball has hit green rectangle.")
case realRedBallCategory | redRectangleCategory:
print("Alive! Red ball has hit red rectangle.")
case blueBallCategory | redRectangleCategory:
print("dead")
case blueBallCategory | greenRectangleCategory:
print("dead")
case realRedBallCategory | blueRectangleCategory:
print("dead")
case realRedBallCategory | greenRectangleCategory:
print("dead")
case greenBallCategory | redRectangleCategory:
print("dead")
case greenBallCategory | blueRectangleCategory:
print("dead")
default:
print("missed")
}
}
func spawnRectangles() {
redRectangle.position = CGPoint(x: 0, y: -400)
redRectangle.size = CGSize(width: 200, height: 20)
blueRectangle.position = CGPoint(x: -300, y: -200)
blueRectangle.size = CGSize(width: 200, height: 20)
greenRectangle.position = CGPoint(x: 100, y: -550)
greenRectangle.size = CGSize(width: 200, height: 20)
self.addChild(redRectangle)
self.addChild(blueRectangle)
self.addChild(greenRectangle)
wall1.position = CGPoint(x: -367.04, y: 0)
wall1.size = CGSize(width: 20, height: 1350)
wall1.physicsBody = SKPhysicsBody(rectangleOf: wall1.size)
wall1.physicsBody?.isDynamic = false
wall1.physicsBody?.affectedByGravity = false
self.addChild(wall1)
wall2.position = CGPoint(x: 367.04, y: 0)
wall2.size = CGSize(width: 20, height: 1350)
wall2.physicsBody = SKPhysicsBody(rectangleOf: wall2.size)
wall2.physicsBody?.isDynamic = false
wall2.physicsBody?.affectedByGravity = false
self.addChild(wall2)
top.position = CGPoint(x: 0, y: 657)
top.size = CGSize(width: 765, height: 20)
top.physicsBody = SKPhysicsBody(rectangleOf: top.size)
top.physicsBody?.isDynamic = false
top.physicsBody?.affectedByGravity = false
self.addChild(top)
bottom.size = CGSize(width: 765, height: 20)
bottom.position = CGPoint(x: 0, y: -657)
bottom.physicsBody = SKPhysicsBody(rectangleOf: bottom.size)
bottom.physicsBody?.isDynamic = false
bottom.physicsBody?.affectedByGravity = false
self.addChild(bottom)
}
func physics(){
for ball in balls{
ball.physicsBody = SKPhysicsBody(circleOfRadius: ball.size.height/2)
ball.physicsBody?.contactTestBitMask = blueRectangleCategory | greenRectangleCategory | redRectangleCategory
}
redRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
redRectangle.physicsBody?.affectedByGravity = false
redRectangle.physicsBody?.isDynamic = false
blueRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
blueRectangle.physicsBody?.affectedByGravity = false
blueRectangle.physicsBody?.isDynamic = false
greenRectangle.physicsBody = SKPhysicsBody(rectangleOf: redRectangle.size)
greenRectangle.physicsBody?.isDynamic = false
greenRectangle.physicsBody?.affectedByGravity = false
balls[0].physicsBody?.categoryBitMask = blueBallCategory
balls[1].physicsBody?.categoryBitMask = greenBallCategory
balls[2].physicsBody?.categoryBitMask = realRedBallCategory
redRectangle.physicsBody?.categoryBitMask = redRectangleCategory
blueRectangle.physicsBody?.categoryBitMask = blueRectangleCategory
greenRectangle.physicsBody?.categoryBitMask = greenRectangleCategory
}
func moveRectangles(){
let redMoveRight = SKAction.moveTo(x: 300, duration: 2)
let redMoveLeft = SKAction.moveTo(x: -280, duration: 2)
let redWholeMovement = SKAction.repeatForever(SKAction.sequence([redMoveRight,redMoveLeft]))
redRectangle.run(redWholeMovement)
let blueMoveRight = SKAction.moveTo(x: 300, duration: 2)
let blueMoveLeft = SKAction.moveTo(x: -280, duration: 1.5)
let blueWholeMovement = SKAction.repeatForever(SKAction.sequence([blueMoveRight,blueMoveLeft]))
blueRectangle.run(blueWholeMovement)
let greenMoveRight = SKAction.moveTo(x: 300, duration: 2)
let greenMoveLeft = SKAction.moveTo(x: -280, duration: 1.5)
let greenWholeMovement = SKAction.repeatForever(SKAction.sequence([greenMoveLeft,greenMoveRight]))
greenRectangle.run(greenWholeMovement)
}
func spawnBalls(){
let ball = balls[Int(arc4random_uniform(UInt32(balls.count)))]
ball.position = CGPoint(x: 0, y: 250)
ball.size = CGSize(width: 70, height: 70)
self.addChild(ball)
}
}
I want a new ball to spawn at the top of the screen if a ball hits a same colored rectangle. Right now when I run the app a randomly colored ball is spawned at the top and when the user clicks the ball drops. If the ball makes contact with a moving rectangle of the same color of the ball the game is supposed to keep going. But it just ends after. If anyone could help that would be great.Thanks!
You only spawn one ball in spawnBalls(). So when you touch only one ball appears. You should try to move spawnBalls() to touchesBegan().

How can I make my level menu scrollable vertically?

I have the following level Menu (as seen below). I would like to make it vertically scrollable, resulting in a total height double that of the screen (full scroll height). How can I achieve this?
Below is the code for the image above:
class LevelMenu: SKScene {
let levelButtonSize = SKSpriteNode(imageNamed: "b1").size
let levelButton1: SKSpriteNode = SKSpriteNode(imageNamed: "b1")
let levelButton2: SKSpriteNode = SKSpriteNode(imageNamed: "b2")
let levelButton3: SKSpriteNode = SKSpriteNode(imageNamed: "b3")
let levelButton4: SKSpriteNode = SKSpriteNode(imageNamed: "b4")
let levelButton5: SKSpriteNode = SKSpriteNode(imageNamed: "b5")
let levelButton6: SKSpriteNode = SKSpriteNode(imageNamed: "b6")
let levelButton7: SKSpriteNode = SKSpriteNode(imageNamed: "b7")
let levelButton8: SKSpriteNode = SKSpriteNode(imageNamed: "b8")
let levelButton9: SKSpriteNode = SKSpriteNode(imageNamed: "b9")
let levelButton10: SKSpriteNode = SKSpriteNode(imageNamed: "b10")
let levelButton11: SKSpriteNode = SKSpriteNode(imageNamed: "b11")
let levelButton12: SKSpriteNode = SKSpriteNode(imageNamed: "b12")
override init(size: CGSize){
super.init(size: size)
let bg = SKSpriteNode(imageNamed: "bg")
backgroundImage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(bg)
let column1PosX = levelButtonSize.width*cDiff
let column2PosX = levelButtonSize.width*cDiff + levelButtonSize.width*2.0
let column3PosX = levelButtonSize.width*cDiff + levelButtonSize.width*4.0
let row1PosY = self.frame.height - levelButtonSize.width*1.5
let row2PosY = row1PosY - levelButtonSize.height - levelButtonSize.width*rDiff
let row3PosY = row2PosY - levelButtonSize.height - levelButtonSize.width*rDiff
let row4PosY = row3PosY - levelButtonSize.height - levelButtonSize.width*rDiff
levelButton1.position = CGPoint(x: column1PosX, y: row1PosY)
levelButton1.zPosition = 10
self.addChild(levelButton1)
levelButton2.position = CGPoint(x: column2PosX, y: row1PosY)
self.addChild(levelButton2)
levelButton3.position = CGPoint(x: column3PosX, y: row1PosY)
self.addChild(levelButton3)
levelButton4.position = CGPoint(x: column1PosX, y: row2PosY)
self.addChild(levelButton4)
levelButton5.position = CGPoint(x: column2PosX, y: row2PosY)
self.addChild(levelButton5)
levelButton6.position = CGPoint(x: column3PosX, y: row2PosY)
self.addChild(levelButton6)
levelButton7.position = CGPoint(x: column1PosX, y: row3PosY)
self.addChild(levelButton7)
levelButton8.position = CGPoint(x: column2PosX, y: row3PosY)
self.addChild(levelButton8)
levelButton9.position = CGPoint(x: column3PosX, y: row3PosY)
self.addChild(levelButton9)
levelButton10.position = CGPoint(x: column1PosX, y: row4PosY)
self.addChild(levelButton10)
levelButton11.position = CGPoint(x: column2PosX, y: row4PosY)
self.addChild(levelButton11)
levelButton12.position = CGPoint(x: column3PosX, y: row4PosY)
self.addChild(levelButton12)
}
UPDATE
Based on Ron Myschuk's solution, the code below show's what I've been able to achieve and this link shows a .gif of the issue I am having currently, where the screen scrolls too much at the top of the menu.
class LMScene: SKScene {
let levelButtonSize = SKSpriteNode(imageNamed: "b1").size
let levelButton1: SKSpriteNode = SKSpriteNode(imageNamed: "b1")
let levelButton2: SKSpriteNode = SKSpriteNode(imageNamed: "b2")
let levelButton3: SKSpriteNode = SKSpriteNode(imageNamed: "b3")
let levelButton4: SKSpriteNode = SKSpriteNode(imageNamed: "b4")
let levelButton5: SKSpriteNode = SKSpriteNode(imageNamed: "b5")
let levelButton6: SKSpriteNode = SKSpriteNode(imageNamed: "b6")
let levelButton7: SKSpriteNode = SKSpriteNode(imageNamed: "b7")
let levelButton8: SKSpriteNode = SKSpriteNode(imageNamed: "b8")
let levelButton9: SKSpriteNode = SKSpriteNode(imageNamed: "b9")
let levelButton10: SKSpriteNode = SKSpriteNode(imageNamed: "b10")
let levelButton11: SKSpriteNode = SKSpriteNode(imageNamed: "b11")
let levelButton12: SKSpriteNode = SKSpriteNode(imageNamed: "b12")
let levelButton13: SKSpriteNode = SKSpriteNode(imageNamed: "b13")
let levelButton14: SKSpriteNode = SKSpriteNode(imageNamed: "b14")
let levelButton15: SKSpriteNode = SKSpriteNode(imageNamed: "b15")
let levelButton16: SKSpriteNode = SKSpriteNode(imageNamed: "b16")
let levelButton17: SKSpriteNode = SKSpriteNode(imageNamed: "b17")
let levelButton18: SKSpriteNode = SKSpriteNode(imageNamed: "b18")
private var scrollCell = SKSpriteNode()
private var moveAmtX: CGFloat = 0
private var moveAmtY: CGFloat = 0
private let minimum_detect_distance: CGFloat = 30
private var initialPosition: CGPoint = CGPoint.zero
private var initialTouch: CGPoint = CGPoint.zero
private var resettingSlider = false
override init(size: CGSize){
super.init(size: size)
scrollCell = SKSpriteNode(color: .blue, size: CGSize(width: self.size.width, height: 2*self.size.height - self.frame.width*0.24734))
scrollCell.position = CGPoint(x: 0, y: 0)
scrollCell.anchorPoint = CGPoint.zero
scrollCell.zPosition = 0
self.addChild(scrollCell)
let backgroundImage = SKSpriteNode(imageNamed: "bg")
backgroundImage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
self.addChild(backgroundImage)
let column1PosX = levelButtonSize.width/2 + self.frame.width*0.14855
let column2PosX = 3*levelButtonSize.width/2 + 2*self.frame.width*0.14855
let column3PosX = 5*levelButtonSize.width/2 + 3*self.frame.width*0.14855
let row1PosY = self.frame.height - levelButtonSize.height/2 - self.frame.width*0.24734
let row2PosY = row1PosY - levelButtonSize.height - self.frame.width*0.24734
let row3PosY = row2PosY - levelButtonSize.height - self.frame.width*0.24734
let row4PosY = row3PosY - levelButtonSize.height - self.frame.width*0.24734
let row5PosY = row4PosY - levelButtonSize.height - self.frame.width*0.24734
let row6PosY = row5PosY - levelButtonSize.height - self.frame.width*0.24734
levelButton1.position = CGPoint(x: column1PosX, y: row1PosY)
levelButton1.zPosition = 10
scrollCell.addChild(levelButton1)
levelButton2.position = CGPoint(x: column2PosX, y: row1PosY)
levelButton2.zPosition = 10
scrollCell.addChild(levelButton2)
levelButton3.position = CGPoint(x: column3PosX, y: row1PosY)
levelButton3.zPosition = 10
scrollCell.addChild(levelButton3)
levelButton4.position = CGPoint(x: column1PosX, y: row2PosY)
levelButton4.zPosition = 10
scrollCell.addChild(levelButton4)
levelButton5.position = CGPoint(x: column2PosX, y: row2PosY)
levelButton5.zPosition = 10
scrollCell.addChild(levelButton5)
levelButton6.position = CGPoint(x: column3PosX, y: row2PosY)
levelButton6.zPosition = 10
scrollCell.addChild(levelButton6)
levelButton7.position = CGPoint(x: column1PosX, y: row3PosY)
levelButton7.zPosition = 10
scrollCell.addChild(levelButton7)
levelButton8.position = CGPoint(x: column2PosX, y: row3PosY)
levelButton8.zPosition = 10
scrollCell.addChild(levelButton8)
levelButton9.position = CGPoint(x: column3PosX, y: row3PosY)
levelButton9.zPosition = 10
scrollCell.addChild(levelButton9)
levelButton10.position = CGPoint(x: column1PosX, y: row4PosY)
levelButton10.zPosition = 10
scrollCell.addChild(levelButton10)
levelButton11.position = CGPoint(x: column2PosX, y: row4PosY)
levelButton11.zPosition = 10
scrollCell.addChild(levelButton11)
levelButton12.position = CGPoint(x: column3PosX, y: row4PosY)
levelButton12.zPosition = 10
scrollCell.addChild(levelButton12)
levelButton13.position = CGPoint(x: column1PosX, y: row5PosY)
levelButton13.zPosition = 10
scrollCell.addChild(levelButton13)
levelButton14.position = CGPoint(x: column2PosX, y: row5PosY)
levelButton14.zPosition = 10
scrollCell.addChild(levelButton14)
levelButton15.position = CGPoint(x: column3PosX, y: row5PosY)
levelButton15.zPosition = 10
scrollCell.addChild(levelButton15)
levelButton16.position = CGPoint(x: column1PosX, y: row6PosY)
levelButton16.zPosition = 10
scrollCell.addChild(levelButton16)
levelButton17.position = CGPoint(x: column2PosX, y: row6PosY)
levelButton17.zPosition = 10
scrollCell.addChild(levelButton17)
levelButton18.position = CGPoint(x: column3PosX, y: row6PosY)
levelButton18.zPosition = 10
scrollCell.addChild(levelButton18)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
if let touch = touches.first as UITouch! {
self.scrollCell.removeAllActions()
initialTouch = touch.location(in: self.scene!.view)
moveAmtY = 0
initialPosition = self.scrollCell.position
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let movingPoint: CGPoint = touch.location(in: self.scene!.view)
moveAmtY = movingPoint.y - initialTouch.y
scrollCell.position = CGPoint(x: initialPosition.x, y: initialPosition.y - moveAmtY)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
checkForResettingSlider()
yMoveActions(moveTo: -moveAmtY)
}
func checkForResettingSlider() {
if resettingSlider { return }
let scrollDif: CGFloat = (scrollCell.size.height - self.size.height) / 2.0
if scrollCell.position.y > scrollDif {
let move: SKAction = SKAction.moveTo(y: scrollDif, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move, completion: { self.resettingSlider = false })
}
if scrollCell.position.y < -scrollDif {
let move: SKAction = SKAction.moveTo(y: 0 - scrollDif, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move, completion: { self.resettingSlider = false })
}
}
func yMoveActions(moveTo: CGFloat) {
let move: SKAction = SKAction.moveBy(x: 0, y: (moveTo * 1.5), duration: 0.3)
move.timingMode = .easeOut
self.scrollCell.run(move, completion: { self.checkForResettingSlider() })
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here is some code that I use to scroll vertically, I've adapted it to fit your menu. It doesn't perfectly line up to all your items, but it'll give you somewhere to start. And it'll show you how to figure this out on your own.
EDIT: I've updated the code use these funds instead. Still declare your buttons the same way but call my createMenu func to actually create the menu. It is looped so it auto adjusts if you change the number of menu items. the only thing you have to be aware of is; if you add or remove buttons change the array at the top of createMenu accordingly. Also adjust the padding variable to how much vertical space you want between the items
func createMenu() {
let buttons = [levelButton1, levelButton2, levelButton3, levelButton4, levelButton5, levelButton6, levelButton7, levelButton8, levelButton9, levelButton10, levelButton11, levelButton12, levelButton13, levelButton14, levelButton15, levelButton16, levelButton17, levelButton18]
let padding: CGFloat = 400
let numberOfRows = CGFloat(buttons.count / 3)
scrollCell = SKSpriteNode(color: .blue, size: CGSize(width: 1024, height: levelButtonSize.height * numberOfRows + padding * numberOfRows))
scrollCell.position = CGPoint(x: 0 - self.size.width / 4, y: 0 - (scrollCell.size.height - self.size.height / 2))
scrollCell.anchorPoint = CGPoint.zero
scrollCell.zPosition = 0
self.addChild(scrollCell)
// let backgroundImage = SKSpriteNode(imageNamed: "bg")
// backgroundImage.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
// self.addChild(backgroundImage)
let column1PosX = scrollCell.size.width / 3 / 2
let column2PosX = scrollCell.size.width / 2
let column3PosX = scrollCell.size.width / 3 / 2 + scrollCell.size.width / 3 * 2
var colCount = 0
var rowCount = 0
for button in buttons {
var posX: CGFloat = column2PosX
if colCount == 0 {
posX = column1PosX
}
else if colCount == 2 {
posX = column3PosX
colCount = -1
}
let indexOffset = CGFloat(rowCount) * (levelButtonSize.height + padding)
let posY = scrollCell.size.height - levelButtonSize.height / 2 - (indexOffset + padding / 2)
button.position = CGPoint(x: posX, y: posY)
button.setScale(0.5)
button.zPosition = 10
scrollCell.addChild(button)
if colCount == -1 {
rowCount += 1
}
colCount += 1
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
self.scrollCell.removeAllActions()
initialTouch = touch.location(in: self.scene!.view)
moveAmtY = 0
initialPosition = self.scrollCell.position
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first as UITouch! {
let movingPoint: CGPoint = touch.location(in: self.scene!.view)
moveAmtY = movingPoint.y - initialTouch.y
scrollCell.position = CGPoint(x: initialPosition.x, y: initialPosition.y - moveAmtY)
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
checkForResettingSlider()
yMoveActions(moveTo: -moveAmtY)
}
func checkForResettingSlider() {
let topPos: CGFloat = scrollCell.size.height - self.size.height / 2
let bottomPos = 0 - (self.size.height / 2)
if scrollCell.position.y > bottomPos {
let move = SKAction.moveTo(y: bottomPos, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move)
}
if scrollCell.position.y < -topPos {
let move = SKAction.moveTo(y: -topPos, duration: 0.3)
move.timingMode = .easeOut
scrollCell.run(move)
}
}
func yMoveActions(moveTo: CGFloat) {
let move = SKAction.moveBy(x: 0, y: (moveTo * 1.5), duration: 0.3)
move.timingMode = .easeOut
self.scrollCell.run(move, completion: { self.checkForResettingSlider() })
}

SKPhysics Contact not functioning properly

I am trying to use the SKPhysicsContactDelegate function in SpriteKit and it will not seem to work. I want one sprite to perform an action when it hits the other. I have set up breakpoints at the didBeginContact function and for some reason my application never calls this function. All help appreciated. Code posted below.
struct PhysicsCatagory {
static let Enemy :UInt32 = 0x1 << 0
static let Slider :UInt32 = 0x1 << 1
static let Circle :UInt32 = 0x1 << 2
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var EnemyTimer = NSTimer()
var Circle = SKSpriteNode()
var Slider = SKSpriteNode()
var FastButton = SKNode()
var Title = SKSpriteNode()
var Text = SKSpriteNode()
var Path = UIBezierPath()
var gameStarted = Bool()
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
self.backgroundColor = UIColor.whiteColor()
Circle = SKSpriteNode(imageNamed:"blueCircle")
Circle.size = CGSize(width: 140, height: 140)
Circle.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
Circle.zPosition = 1.0
self.addChild(Circle)
Slider = SKSpriteNode(imageNamed: "blocker1")
Slider.size = CGSize(width: 15, height: 50)
Slider.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2 + 80)
addChild(Slider)
Slider.zPosition = 1.0
moveClockWise()
}
func didBeginContact(contact: SKPhysicsContact) {
if contact.bodyA.node != nil && contact.bodyB.node != nil{
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
if ((firstBody.name == "Enemy") && (secondBody.name == "Slider")){
collisionBall(firstBody, Slider: secondBody)
}
else if ((firstBody.name == "Slider") && (secondBody.name == "Enemy")) {
collisionBall(secondBody, Slider: firstBody)
}
}
}
func collisionBall(Enemy : SKSpriteNode, Slider : SKSpriteNode){
Enemy.physicsBody?.dynamic = true
Enemy.physicsBody?.affectedByGravity = true
Enemy.physicsBody?.mass = 4.0
Slider.physicsBody?.mass = 4.0
Enemy.removeAllActions()
Enemy.physicsBody?.contactTestBitMask = 0
Enemy.physicsBody?.collisionBitMask = 0
Enemy.name = nil
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
//Slider.hidden = false
FastButton.hidden = false
Title.hidden = true
Text.hidden = true
EnemyTimer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(GameScene.Enemies), userInfo: nil, repeats: true)
//Physics
Slider.physicsBody?.categoryBitMask = PhysicsCatagory.Slider
Slider.physicsBody?.collisionBitMask = PhysicsCatagory.Enemy
Slider.physicsBody?.contactTestBitMask = PhysicsCatagory.Enemy
Slider.name = "Slider"
Slider.physicsBody?.dynamic = true
Slider.physicsBody?.affectedByGravity = true
if gameStarted == false{
gameStarted = true
}
else if gameStarted == true{
}
}
func moveClockWise(){
let dx = Slider.position.x / 2
let dy = Slider.position.y / 2
let rad = atan2(dy, dx)
let Path = UIBezierPath(arcCenter: CGPoint(x: self.frame.width / 2, y: self.frame.height / 2), radius: 90, startAngle: rad, endAngle: rad + CGFloat(M_PI * 4), clockwise: true)
let follow = SKAction.followPath(Path.CGPath, asOffset: false, orientToPath: true, speed: 150)
//let rotate = SKAction.rotateByAngle(75, duration: 100)
Slider.runAction(SKAction.repeatActionForever(follow).reversedAction())
//Slider.runAction(SKAction.repeatActionForever(rotate).reversedAction())
}
func Enemies(){
let Enemy = SKSpriteNode(imageNamed: "darkRedDot")
Enemy.size = CGSize(width: 20, height: 20)
//Physics
Enemy.physicsBody = SKPhysicsBody(circleOfRadius: Enemy.size.width / 2)
Enemy.physicsBody?.categoryBitMask = PhysicsCatagory.Enemy
Enemy.physicsBody?.contactTestBitMask = PhysicsCatagory.Slider //| PhysicsCatagory.Circle
Enemy.physicsBody?.collisionBitMask = PhysicsCatagory.Slider //| PhysicsCatagory.Circle
Enemy.physicsBody?.dynamic = true
Enemy.physicsBody?.affectedByGravity = false
Enemy.name = "Enemy"
The contact is not beginning because your slider has no physics body, making it impossible to recognize contact. Unless, it's not in this code, your slider has no physics body, but your enemy does. Try declaring Slider.phisicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "blocker1"), size: Slider.size)
Also, you should read about standard naming conventions in Swift. It is recommended that your variables always start with lowercase letters (eg. enemyTimer, circle, slider, fastButton, etc...).