Sprite Kit Won't stop adding nodes - ios

In my project everything is going smoothly and working great except for when I want to add 1 instance of a SKLabelNode when a certain event happens in my game.
Then problem is when the event happens it adds the SKLabelNode more than once and it keeps on doing it into the thousands...
Here is my code:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
var isTouched: Bool = false
let lavaRoom = SKSpriteNode(imageNamed: "LavaRom")
let tileStone1 = SKSpriteNode(imageNamed: "TileStone")
let tileStone2 = SKSpriteNode(imageNamed: "TileStone")
let tileStone3 = SKSpriteNode(imageNamed: "TileStone")
let tileStone4 = SKSpriteNode(imageNamed: "TileStone")
let tileStone5 = SKSpriteNode(imageNamed: "TileStone")
let tileStone6 = SKSpriteNode(imageNamed: "TileStone")
let tileStone7 = SKSpriteNode(imageNamed: "TileStone")
let tileStone8 = SKSpriteNode(imageNamed: "TileStone")
let tileStone9 = SKSpriteNode(imageNamed: "TileStone")
let tileStone10 = SKSpriteNode(imageNamed: "TileStone")
let tileStone11 = SKSpriteNode(imageNamed: "TileStone")
let tileStone12 = SKSpriteNode(imageNamed: "TileStone")
let tileStone13 = SKSpriteNode(imageNamed: "TileStone")
let tileStone14 = SKSpriteNode(imageNamed: "TileStone")
let tileStone15 = SKSpriteNode(imageNamed: "TileStone")
let tileStone16 = SKSpriteNode(imageNamed: "TileStone")
let tileStone17 = SKSpriteNode(imageNamed: "TileStone")
let tileStone18 = SKSpriteNode(imageNamed: "TileStone")
let tileStone19 = SKSpriteNode(imageNamed: "TileStone")
let tileStone20 = SKSpriteNode(imageNamed: "TileStone")
let tileStone21 = SKSpriteNode(imageNamed: "TileStone")
let tileStone22 = SKSpriteNode(imageNamed: "TileStone")
let tileStone23 = SKSpriteNode(imageNamed: "TileStone")
let tileStone24 = SKSpriteNode(imageNamed: "TileStone")
let tileStone25 = SKSpriteNode(imageNamed: "TileStone")
let tileStone26 = SKSpriteNode(imageNamed: "TileStone")
let tileStone27 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop1 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop2 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop3 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop4 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop5 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop6 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop7 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop8 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop9 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop10 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop11 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop12 = SKSpriteNode(imageNamed: "TileStone")
let tileStoneTop13 = SKSpriteNode(imageNamed: "TileStone")
let player = SKSpriteNode(imageNamed: "PlayerBox")
let enemyBox = SKSpriteNode(imageNamed: "TileStone")
let playerInt : UInt32 = 0
let enemyInt : UInt32 = 1
override func didMoveToView(view: SKView) {
let invisibleNode = SKShapeNode(rect: CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height))
invisibleNode.name = "box"
invisibleNode.fillColor = SKColor.redColor()
invisibleNode.strokeColor = SKColor.clearColor()
self.addChild(invisibleNode)
self.backgroundColor = UIColor(hue: 0.0194, saturation: 0.66, brightness: 0.89, alpha: 1.0)
player.size = CGSize(width: 50, height: 50)
player.position = CGPointMake(self.frame.size.width / 2 - 350, 190)
player.anchorPoint = CGPointZero
player.zPosition = 2
addChild(player)
enemyBox.size = CGSize(width: 50, height: 50)
enemyBox.position = CGPointMake(self.frame.size.width / 2, 215)
enemyBox.zPosition = 2
addChild(enemyBox)
tileStone1.size = CGSize(width: 100, height: 100)
tileStone1.anchorPoint = CGPointZero
tileStone1.position = CGPoint(x: 0, y: 90)
tileStone1.zPosition = 2
addChild(tileStone1)
tileStone2.size = CGSize(width: 100, height: 100)
tileStone2.zPosition = 2
tileStone2.anchorPoint = CGPointZero
tileStone2.position = CGPoint(x: 100, y: 90)
addChild(tileStone2)
tileStone3.size = CGSize(width: 100, height: 100)
tileStone3.zPosition = 2
tileStone3.anchorPoint = CGPointZero
tileStone3.position = CGPoint(x: 200, y: 90)
addChild(tileStone3)
tileStone4.size = CGSize(width: 100, height: 100)
tileStone4.zPosition = 2
tileStone4.anchorPoint = CGPointZero
tileStone4.position = CGPoint(x: 300, y: 90)
addChild(tileStone4)
tileStone5.size = CGSize(width: 100, height: 100)
tileStone5.anchorPoint = CGPointZero
tileStone5.position = CGPoint(x: 400, y: 90)
tileStone5.zPosition = 2
addChild(tileStone5)
tileStone6.size = CGSize(width: 100, height: 100)
tileStone6.anchorPoint = CGPointZero
tileStone6.position = CGPoint(x: 500, y: 90)
tileStone6.zPosition = 2
addChild(tileStone6)
tileStone7.size = CGSize(width: 100, height: 100)
tileStone7.anchorPoint = CGPointZero
tileStone7.position = CGPoint(x: 600, y: 90)
tileStone7.zPosition = 2
addChild(tileStone7)
tileStone8.size = CGSize(width: 100, height: 100)
tileStone8.anchorPoint = CGPointZero
tileStone8.position = CGPoint(x: 700, y: 90)
tileStone8.zPosition = 2
addChild(tileStone8)
tileStone9.size = CGSize(width: 100, height: 100)
tileStone9.anchorPoint = CGPointZero
tileStone9.position = CGPoint(x: 800, y: 90)
tileStone9.zPosition = 2
addChild(tileStone9)
tileStone10.size = CGSize(width: 100, height: 100)
tileStone10.anchorPoint = CGPointZero
tileStone10.position = CGPoint(x: 900, y: 90)
tileStone10.zPosition = 2
addChild(tileStone10)
addChild(tileStone11)
tileStone11.size = CGSize(width: 100, height: 100)
tileStone11.anchorPoint = CGPointZero
tileStone11.position = CGPoint(x: 1000, y: 90)
tileStone11.zPosition = 2
tileStone12.size = CGSize(width: 100, height: 100)
tileStone12.anchorPoint = CGPointZero
tileStone12.position = CGPoint(x: 1100, y: 90)
tileStone12.zPosition = 2
addChild(tileStone12)
tileStone13.size = CGSize(width: 100, height: 100)
tileStone13.anchorPoint = CGPointZero
tileStone13.position = CGPoint(x: 1200, y: 90)
tileStone13.zPosition = 2
addChild(tileStone13)
tileStone14.size = CGSize(width: 100, height: 100)
tileStone14.anchorPoint = CGPointZero
tileStone14.position = CGPoint(x: 1300, y: 90)
tileStone14.zPosition = 2
addChild(tileStone14)
tileStone15.size = CGSize(width: 100, height: 100)
tileStone15.anchorPoint = CGPointZero
tileStone15.position = CGPoint(x: 1400, y: 90)
tileStone15.zPosition = 2
addChild(tileStone15)
tileStone16.size = CGSize(width: 100, height: 100)
tileStone16.anchorPoint = CGPointZero
tileStone16.position = CGPoint(x: 1500, y: 90)
tileStone16.zPosition = 2
addChild(tileStone16)
tileStone17.size = CGSize(width: 100, height: 100)
tileStone17.anchorPoint = CGPointZero
tileStone17.position = CGPoint(x: 1600, y: 90)
tileStone17.zPosition = 2
addChild(tileStone17)
tileStone18.size = CGSize(width: 100, height: 100)
tileStone18.anchorPoint = CGPointZero
tileStone18.position = CGPoint(x: 1700, y: 90)
tileStone18.zPosition = 2
addChild(tileStone18)
tileStone19.size = CGSize(width: 100, height: 100)
tileStone19.anchorPoint = CGPointZero
tileStone19.position = CGPoint(x: 1800, y: 90)
tileStone19.zPosition = 2
addChild(tileStone19)
tileStone20.size = CGSize(width: 100, height: 100)
tileStone20.anchorPoint = CGPointZero
tileStone20.position = CGPoint(x: 1900, y: 90)
tileStone20.zPosition = 2
addChild(tileStone20)
tileStone21.size = CGSize(width: 100, height: 100)
tileStone21.anchorPoint = CGPointZero
tileStone21.position = CGPoint(x: 2000, y: 90)
tileStone21.zPosition = 2
addChild(tileStone21)
tileStone22.size = CGSize(width: 100, height: 100)
tileStone22.anchorPoint = CGPointZero
tileStone22.position = CGPoint(x: 2100, y: 90)
tileStone22.zPosition = 2
addChild(tileStone22)
tileStoneTop1.size = CGSize(width: 100, height: 100)
tileStoneTop1.anchorPoint = CGPointZero
tileStoneTop1.position = CGPoint(x: 0, y: 580)
tileStoneTop1.zPosition = 2
addChild(tileStoneTop1)
tileStoneTop2.size = CGSize(width: 100, height: 100)
tileStoneTop2.zPosition = 2
tileStoneTop2.anchorPoint = CGPointZero
tileStoneTop2.position = CGPoint(x: 100, y: 580)
addChild(tileStoneTop2)
tileStoneTop3.size = CGSize(width: 100, height: 100)
tileStoneTop3.zPosition = 2
tileStoneTop3.anchorPoint = CGPointZero
tileStoneTop3.position = CGPoint(x: 200, y: 580)
addChild(tileStoneTop3)
tileStoneTop4.size = CGSize(width: 100, height: 100)
tileStoneTop4.zPosition = 2
tileStoneTop4.anchorPoint = CGPointZero
tileStoneTop4.position = CGPoint(x: 300, y: 580)
addChild(tileStoneTop4)
tileStoneTop5.size = CGSize(width: 100, height: 100)
tileStoneTop5.anchorPoint = CGPointZero
tileStoneTop5.position = CGPoint(x: 400, y: 580)
tileStoneTop5.zPosition = 2
addChild(tileStoneTop5)
tileStoneTop6.size = CGSize(width: 100, height: 100)
tileStoneTop6.anchorPoint = CGPointZero
tileStoneTop6.position = CGPoint(x: 500, y: 580)
tileStoneTop6.zPosition = 2
addChild(tileStoneTop6)
tileStoneTop7.size = CGSize(width: 100, height: 100)
tileStoneTop7.anchorPoint = CGPointZero
tileStoneTop7.position = CGPoint(x: 600, y: 580)
tileStoneTop7.zPosition = 2
addChild(tileStoneTop7)
tileStoneTop8.size = CGSize(width: 100, height: 100)
tileStoneTop8.anchorPoint = CGPointZero
tileStoneTop8.position = CGPoint(x: 700, y: 580)
tileStoneTop8.zPosition = 2
addChild(tileStoneTop8)
tileStoneTop9.size = CGSize(width: 100, height: 100)
tileStoneTop9.anchorPoint = CGPointZero
tileStoneTop9.position = CGPoint(x: 800, y: 580)
tileStoneTop9.zPosition = 2
addChild(tileStoneTop9)
tileStoneTop10.size = CGSize(width: 100, height: 100)
tileStoneTop10.anchorPoint = CGPointZero
tileStoneTop10.position = CGPoint(x: 900, y: 580)
tileStoneTop10.zPosition = 2
addChild(tileStoneTop10)
tileStoneTop11.size = CGSize(width: 150, height: 100)
tileStoneTop11.anchorPoint = CGPointZero
tileStoneTop11.position = CGPoint(x: 1000, y: 580)
tileStoneTop11.zPosition = 2
addChild(tileStoneTop11)
tileStoneTop12.size = CGSize(width: 200, height: 100)
tileStoneTop12.anchorPoint = CGPointZero
tileStoneTop12.position = CGPoint(x: 1100, y: 5780)
tileStoneTop12.zPosition = 2
addChild(tileStoneTop12)
tileStoneTop13.size = CGSize(width: 100, height: 100)
tileStoneTop13.anchorPoint = CGPointZero
tileStoneTop13.position = CGPoint(x: 1100, y: 5780)
tileStoneTop13.zPosition = 2
// addChild(tileStoneTop13)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let touchLocation = touch.locationInNode(self)
let touchedNode = nodeAtPoint(touchLocation)
if touchedNode.name == "box" {
isTouched = true
}
if touchedNode.name == "rerun"{
for node in self.nodesAtPoint(touchLocation) {
if node.name == "rerun" {
isTouched = false
node.removeFromParent()
}
}
}
}
}
let moveFactor:CGFloat = 0.5
override func update(currentTime: CFTimeInterval) {
var tileStoneArray = [tileStone1, tileStone2, tileStone3, tileStone4, tileStone5, tileStone6, tileStone7, tileStone8, tileStone9, tileStone10, tileStone11, tileStone12, tileStone13, tileStone14, tileStone15, tileStone16, tileStone17, tileStone18, tileStone19, tileStone20, tileStone21, tileStone22, tileStoneTop1, tileStoneTop2, tileStoneTop3, tileStoneTop4, tileStoneTop5, tileStoneTop6, tileStoneTop7, tileStoneTop8, tileStoneTop9, tileStoneTop10, tileStoneTop11, tileStoneTop12, tileStoneTop13]
if isTouched == false {
enemyBox.position = CGPoint(x: enemyBox.position.x - 10, y: 215)
}
if enemyBox.position.x < self.frame.minX - 100 {
enemyBox.position = CGPoint(x: self.frame.maxX, y: 215)
}
for tileStone1 in tileStoneArray {
if isTouched == false {
tileStone1.position = CGPoint(x: tileStone1.position.x - 10, y: tileStone1.position.y)
}
if (tileStone1.position.x < self.frame.minX - 100){
tileStone1.anchorPoint = CGPointZero
tileStone1.position = CGPoint(x: self.frame.maxX, y: tileStone1.position.y)
}
}
var score = 0;
func addGreatNode(){
let congratsLabel = SKLabelNode(fontNamed: "")
congratsLabel.name = "rerun"
congratsLabel.text = "Great!"
congratsLabel.fontSize = 65
congratsLabel.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2)
congratsLabel.fontColor = SKColor.yellowColor()
addChild(congratsLabel)
}
if isTouched == true {
var xValuesEnemy = enemyBox.position.x
var xValuesPlayer = player.position.x
if xValuesPlayer < xValuesEnemy + 120 && xValuesPlayer > xValuesEnemy - 120 {
addGreatNode()
//This is where it adds the node infinitely
}
else {
}
}
}
}
This is a image of my node count climbing while in my game.
I know that I could improve my code to be more efficient but I'm very inexperienced, but I'm working on it.

Your addGreatNode() function is being called within update: which is called by the scene up to 60 times per second.
A few more tips:
Keep your TileStones in a collection
When adding TileStones to your Scene, use either a for or while loop. This will compact your code. Within each iteration, adjust the position for each Node.
If you want to add a SKLabelNode on touch, that logic should be within touchesBegan:, and not associated with update:

Related

Animation with UIBezierPath - Object gets fixed on the top left corner

I am trying to create an animation in swift to make a few ballons float from the bottom of the screen to the top. However in the middle of the animation one of the balloons gets fixed on the top left corner of the screen and does not disappear even when the animation finishes.
Please watch this video to see what I am talking about:
https://imgur.com/a/tyS0Q5u
Here is my code. I don't really know what I am doing wrong.
func presentVictoryView() {
blackView.isHidden = false
for _ in 0 ... 20 {
let objectView = UIView()
objectView.translatesAutoresizingMaskIntoConstraints = false
objectView.frame = CGRect(x: 50, y: 50, width: 20, height: 100)
//objectView.backgroundColor = .clear
//objectView.alpha = CGFloat(0.9)
objectView.isHidden = false
let ballon = UILabel()
ballon.translatesAutoresizingMaskIntoConstraints = false
ballon.frame = CGRect(x: 50, y: 50, width: 20, height: 100)
//ballon.backgroundColor = .clear
//ballon.alpha = CGFloat(0.9)
ballon.text = "🎈"
ballon.font = UIFont.systemFont(ofSize: 60)
objectView.addSubview(ballon)
NSLayoutConstraint.activate([
ballon.centerXAnchor.constraint(equalTo: objectView.centerXAnchor),
ballon.centerYAnchor.constraint(equalTo: objectView.centerYAnchor)
])
blackView.addSubview(objectView)
let randomXOffset = Int.random(in: -120 ..< 200)
let path = UIBezierPath()
path.move(to: CGPoint(x: 270 + randomXOffset, y: 1000))
path.addCurve(to: CGPoint(x: 100 + randomXOffset, y: -300), controlPoint1: CGPoint(x: 300 - randomXOffset, y: 600), controlPoint2: CGPoint(x: 70 + randomXOffset, y: 300))
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.repeatCount = 1
animation.duration = Double.random(in: 4.0 ..< 7.0)
//animation.timeOffset = Double(arc4random_uniform(50))
objectView.layer.add(animation, forKey: "animate position along path")
}
//objectView.isHidden = true
//self?.newGame()
}
Thank you for your help! :)
You should use delegate to detect end of animation and remove bubbleView then.
func presentVictoryView() {
blackView.isHidden = false
for _ in 0 ... 20 {
let objectView = UIView()
objectView.translatesAutoresizingMaskIntoConstraints = false
objectView.frame = CGRect(x: 50, y: 50, width: 20, height: 100)
//objectView.backgroundColor = .clear
//objectView.alpha = CGFloat(0.9)
objectView.isHidden = false
let ballon = UILabel()
ballon.translatesAutoresizingMaskIntoConstraints = false
ballon.frame = CGRect(x: 50, y: 50, width: 20, height: 100)
//ballon.backgroundColor = .clear
//ballon.alpha = CGFloat(0.9)
ballon.text = "🎈"
ballon.font = UIFont.systemFont(ofSize: 60)
objectView.addSubview(ballon)
NSLayoutConstraint.activate([
ballon.centerXAnchor.constraint(equalTo: objectView.centerXAnchor),
ballon.centerYAnchor.constraint(equalTo: objectView.centerYAnchor)
])
blackView.addSubview(objectView)
let randomXOffset = Int.random(in: -120 ..< 200)
let path = UIBezierPath()
path.move(to: CGPoint(x: 270 + randomXOffset, y: 1000))
path.addCurve(to: CGPoint(x: 100 + randomXOffset, y: -300), controlPoint1: CGPoint(x: 300 - randomXOffset, y: 600), controlPoint2: CGPoint(x: 70 + randomXOffset, y: 300))
let animation = CAKeyframeAnimation(keyPath: "position")
animation.path = path.cgPath
animation.repeatCount = 1
// add these
animation.fillMode = .forwards
animation.isRemovedOnCompletion = false
let delegate = BubbleAnimDelegate()
delegate.didFinishAnimation = {
objectView.removeFromSuperview()
}
animation.delegate = delegate
// upto here
animation.duration = Double.random(in: 4.0 ..< 7.0)
//animation.timeOffset = Double(arc4random_uniform(50))
objectView.layer.add(animation, forKey: "animate position along path")
}
//objectView.isHidden = true
//self?.newGame()
}
class BubbleAnimDelegate: NSObject, CAAnimationDelegate {
var didFinishAnimation: (()->Void)?
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
didFinishAnimation?()
}
}

What is the difference between SKScene.size.width SKSpriteNode.size.width?

I'm new to swift 5.
I printed out the self.size.width in GameScene and the result is 677.0
I printed out the self.size.width from another class - lets say Ground and the result is 4002.0
I'm confused, please help.
Thanks a lot.
GameScene.swift:
import SpriteKit
class GameScene: SKScene {
let cam = SKCameraNode()
let bee = SKSpriteNode()
let ground = Ground()
override func didMove(to view: SKView) {
self.anchorPoint = .zero
//self.backgroundColor = UIColor(red: 0.4, green: 0.6, blue: 0.95, alpha: 1.0)
self.camera = cam
self.addingTheFlyingBee()
self.addBackground()
let bee2 = Bee()
bee2.position = CGPoint(x: 325, y: 325)
self.addChild(bee2)
let bee3 = Bee()
bee3.position = CGPoint(x: 200, y: 325)
self.addChild(bee3)
ground.position = CGPoint(x: -self.size.width * 2, y: 0)
ground.size = CGSize(width: self.size.width * 6, height: 0)
ground.createChildren()
self.addChild(ground)
}
override func didSimulatePhysics() {
self.camera!.position = bee.position
}
func addBackground() {
let bg = SKSpriteNode(imageNamed: "background-menu")
bg.position = CGPoint(x: 220, y: 220)
bg.zPosition = -1
self.addChild(bg)
}
func addingTheFlyingBee() {
bee.position = CGPoint(x: 250, y: 250)
bee.size = CGSize(width: 38, height: 34)
self.addChild(bee)
let beeAtlas = SKTextureAtlas(named: "Enemies")
let beeFrames : [SKTexture] = [
beeAtlas.textureNamed("bee"),
beeAtlas.textureNamed("bee-fly")
]
let flyAction = SKAction.animate(with: beeFrames, timePerFrame: 0.14)
let beeAction = SKAction.repeatForever(flyAction)
bee.run(beeAction)
let pathLeft = SKAction.moveBy(x: -200, y: -10, duration: 2)
let pathRight = SKAction.moveBy(x: 200, y: 10, duration: 2)
let flipTextureNegative = SKAction.scaleX(to: -1, duration: 0)
let flipTexturePositive = SKAction.scaleX(to: 1, duration: 0)
let flightOfTheBee = SKAction.sequence([ pathLeft, flipTextureNegative, pathRight, flipTexturePositive])
let neverEndingFlight = SKAction.repeatForever(flightOfTheBee)
bee.run(neverEndingFlight)
}
Ground.swift:
import Foundation
import SpriteKit
class Ground: SKSpriteNode, GameSprite {
var textureAtlas: SKTextureAtlas = SKTextureAtlas(named: "Environment")
var initialSize = CGSize.zero
func createChildren() {
self.anchorPoint = CGPoint(x: 0, y: 1)
let texture = textureAtlas.textureNamed("ground")
var tileCount: CGFloat = 0
let tileSize = CGSize(width: 35, height: 300)
while tileCount * tileSize.width < self.size.width {
let tileNode = SKSpriteNode(texture: texture)
tileNode.size = tileSize
tileNode.position.x = tileCount * tileSize.width
tileNode.anchorPoint = CGPoint(x: 0, y: 1)
self.addChild(tileNode)
tileCount += 1
}
}
func onTap() {}
}
I don't really understand the question. Since your scene width is 677.0px and your ground
width should be 6 * scene width so 4062px.
Is your question "Why is ground node width 4002px instead of 4062px?" or "What is the difference between those properties?"
If you question is the second one then this should be an answer:
SKScene.size.width will return width of the current scene while SKSpriteNode.size.width will return width of the target node in your case "ground" node.
self.size.width will return width of the current class you're editing.
So in GameScene.swift file if you call self.size.width inside a class GameScene: SKScene it will return GameScene width.
In Ground.swift file calling self.size.width inside a class Ground: SKSpriteNode, GameSprite will return Ground width.
I hope this answers your question.

Converting CGPoints between UIKit and SpriteKit woes: UIViews next to SKNodes

There are many posts on this but for whatever reason I can't seem to get the correct sequence of conversions correct. I'm trying to get the UISliders to go directly below the SKLabels.
As you can see in the pictures, one of the sliders doesn't show at all, and the other one is in the completely wrong place (the volume slider is near the seek label):
Here is my current attempt at getting this right, though I've tried a few other configurations that got me nowhere:
class GameScene: SKScene {
let seekSlider = UISlider(frame: CGRect(x: 0, y: 0, width: 200, height: 15))
let volSlider = UISlider(frame: CGRect(x: 0, y: 0, width: 200, height: 15))
override func didMove(to view: SKView) {
removeAllChildren() // Delete this in your actual project.
// Add some labels:
let seekLabel = SKLabelNode(text: "Seek")
seekLabel.setScale(3)
seekLabel.verticalAlignmentMode = .center
seekLabel.position = CGPoint(x: frame.minX + seekLabel.frame.width/2,
y: frame.maxY - seekLabel.frame.height)
let volLabel = SKLabelNode(text: "Volume")
volLabel.setScale(3)
volLabel.verticalAlignmentMode = .center
volLabel.position = CGPoint(x: frame.minX + volLabel.frame.width/2,
y: frame.minY + volLabel.frame.height + volSlider.frame.height)
/* CONVERSION WOES BELOW: */
// Configure sliders:
let seekOrigin = convertPoint(toView: convert(seekLabel.position, from: self))
seekSlider.frame = CGRect(origin: seekOrigin, size: CGSize(width: 200, height: 15))
seekSlider.value = 1
let volOrigin = convertPoint(fromView: CGPoint(x: volLabel.frame.minX, y: volLabel.frame.minY))
seekSlider.frame = CGRect(origin: volOrigin, size: CGSize(width: 200, height: 15))
seekSlider.value = 0
// Scene stuff:
view.addSubview(seekSlider)
view.addSubview(volSlider)
addChild(seekLabel)
addChild(volLabel)
}
}
You're very close! There are two minor issues in your code:
Issue #1
You modify the seekSlider's frame twice instead of modifying the seekSlider then the volSlider.
let volOrigin = convertPoint(fromView: CGPoint(x: volLabel.frame.minX, y: volLabel.frame.minY))
seekSlider.frame = CGRect(origin: volOrigin, size: CGSize(width: 200, height: 15))
seekSlider.value = 0
should be
let volOrigin = convertPoint(fromView: CGPoint(x: volLabel.frame.minX, y: volLabel.frame.minY))
volSlider.frame = CGRect(origin: volOrigin, size: CGSize(width: 200, height: 15))
volSlider.value = 0
Issue #2
The conversion code is not correct.
Using the convertPoint(toView method with the node's position should do the trick:
let seekOrigin = convertPoint(toView: seekLabel.position)
Final Code:
class GameScene: SKScene {
let seekSlider = UISlider(frame: CGRect(x: 0, y: 0, width: 200, height: 15))
let volSlider = UISlider(frame: CGRect(x: 0, y: 0, width: 200, height: 15))
override func didMove(to view: SKView) {
removeAllChildren() // Delete this in your actual project.
// Add some labels:
let seekLabel = SKLabelNode(text: "Seek")
seekLabel.setScale(3)
seekLabel.verticalAlignmentMode = .center
seekLabel.position = CGPoint(x: frame.minX + seekLabel.frame.width/2,
y: frame.maxY - seekLabel.frame.height)
let volLabel = SKLabelNode(text: "Volume")
volLabel.setScale(3)
volLabel.verticalAlignmentMode = .center
volLabel.position = CGPoint(x: frame.minX + volLabel.frame.width/2,
y: frame.minY + volLabel.frame.height + volSlider.frame.height)
// Configure sliders:
let seekOrigin = convertPoint(toView: seekLabel.position)
let offsetSeekOrigin = CGPoint(x: seekOrigin.x, y: seekOrigin.y + 50)
seekSlider.frame = CGRect(origin: offsetSeekOrigin, size: CGSize(width: 200, height: 15))
seekSlider.value = 1
let volOrigin = convertPoint(toView: volLabel.position)
let offsetVolOrigin = CGPoint(x: volOrigin.x, y: volOrigin.y - 50)
volSlider.frame = CGRect(origin: offsetVolOrigin, size: CGSize(width: 200, height: 15))
volSlider.value = 0
// Scene stuff:
view.addSubview(seekSlider)
view.addSubview(volSlider)
addChild(seekLabel)
addChild(volLabel)
}
}
Final Result:

Create parallax clouds with random Y coordinate using SKSpriteNode and SKAction in Swift

I'm trying to create a parallax cloud effect with different speeds and sizes but i'm having a hard time when trying to add a different Y coordinate, not always in the same Y for all the clouds.
So I have 5 clouds, with its respective X and Y coordinate, and I use the SKAction.moveByX() function.
All the clouds starts from for example FRAME_WIDTH + 200 (out of bounds) and they end at -100. After that I reset the X to FRAME_WIDTH + 200 and do a "forever" sequence.
That was working perfectly, but I would like to add a random Y coordinate every time the animation finishes.
I was able to do it, but that would only change the Y coordinate once.
How can I achieve this?
Here's my current code:
func addClouds() {
let cloud1 = SKSpriteNode(imageNamed: "cloud1")
let cloud2 = SKSpriteNode(imageNamed: "cloud2")
let cloud3 = SKSpriteNode(imageNamed: "cloud3")
let cloud4 = SKSpriteNode(imageNamed: "cloud4")
let cloud5 = SKSpriteNode(imageNamed: "cloud5")
cloud1.zPosition = ZIndexPosition.CLOUD
cloud1.position = CGPoint(x: self.FRAME_WIDTH - 100, y: self.FRAME_HEIGHT / 2 - 150)
cloud1.size = CGSize(width: 44, height: 14)
cloud2.zPosition = ZIndexPosition.CLOUD
cloud2.position = CGPoint(x: self.FRAME_WIDTH + 150, y: self.FRAME_HEIGHT - 100)
cloud2.size = CGSize(width: 104, height: 16)
cloud3.zPosition = ZIndexPosition.CLOUD
cloud3.position = CGPoint(x: self.FRAME_WIDTH - 50, y: self.FRAME_HEIGHT - 200)
cloud3.size = CGSize(width: 8, height: 6)
cloud4.zPosition = ZIndexPosition.CLOUD
cloud4.position = CGPoint(x: self.FRAME_WIDTH + 200, y: self.FRAME_HEIGHT / 2 - 50)
cloud4.size = CGSize(width: 116, height: 32)
cloud5.zPosition = ZIndexPosition.CLOUD
cloud5.position = CGPoint(x: self.FRAME_WIDTH, y: self.FRAME_HEIGHT - 250)
cloud5.size = CGSize(width: 24, height: 6)
let resetCloud1YPos = SKAction.moveToY(self.randomCloud1, duration: 0)
let resetCloud2YPos = SKAction.moveToY(self.randomCloud2, duration: 0)
let resetCloud3YPos = SKAction.moveToY(self.randomCloud3, duration: 0)
let resetCloud4YPos = SKAction.moveToY(self.randomCloud4, duration: 0)
let resetCloud5YPos = SKAction.moveToY(self.randomCloud5, duration: 0)
let resetCloud1Pos = SKAction.moveToX((self.FRAME_WIDTH * 2) - 100, duration: 0)
let resetCloud2Pos = SKAction.moveToX((self.FRAME_WIDTH * 2) - 150, duration: 0)
let resetCloud3Pos = SKAction.moveToX((self.FRAME_WIDTH * 2) - 50, duration: 0)
let resetCloud4Pos = SKAction.moveToX((self.FRAME_WIDTH * 2) - 200, duration: 0)
let resetCloud5Pos = SKAction.moveToX((self.FRAME_WIDTH * 2) - 20, duration: 0)
let moveCloud1 = SKAction.moveToX(-100, duration: 28)
let cloud1Sequence = SKAction.sequence([moveCloud1, resetCloud1Pos, resetCloud1YPos])
let cloud1Forever = SKAction.repeatActionForever(cloud1Sequence)
let moveCloud2 = SKAction.moveToX(-100, duration: 24)
let cloud2Sequence = SKAction.sequence([moveCloud2, resetCloud2Pos, resetCloud2YPos])
let cloud2Forever = SKAction.repeatActionForever(cloud2Sequence)
let moveCloud3 = SKAction.moveToX(-100, duration: 35)
let cloud3Sequence = SKAction.sequence([moveCloud3, resetCloud3Pos, resetCloud3YPos])
let cloud3Forever = SKAction.repeatActionForever(cloud3Sequence)
let moveCloud4 = SKAction.moveToX(-100, duration: 13)
let cloud4Sequence = SKAction.sequence([moveCloud4, resetCloud4Pos, resetCloud4YPos])
let cloud4Forever = SKAction.repeatActionForever(cloud4Sequence)
let moveCloud5 = SKAction.moveToX(-100, duration: 18)
let cloud5Sequence = SKAction.sequence([moveCloud5, resetCloud5Pos, resetCloud5YPos])
let cloud5Forever = SKAction.repeatActionForever(cloud5Sequence)
cloud1.runAction(cloud1Forever)
cloud2.runAction(cloud2Forever)
cloud3.runAction(cloud3Forever)
cloud4.runAction(cloud4Forever)
cloud5.runAction(cloud5Forever)
self.addChild(cloud1)
self.addChild(cloud2)
self.addChild(cloud3)
self.addChild(cloud4)
self.addChild(cloud5)
}
randomCloud1,2,3,4 and 5 is just a random generation within the screen height bounds.
Thanks in advance.
If I understand what you're trying to accomplish, the following code should help you:
// ViewController.swift
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
//create and position a test SKSpriteNode
let cloud = SKSpriteNode()
cloud.color = SKColor.blueColor()
cloud.position = CGPoint(x: self.frame.size.width - 100, y: 400)
cloud.size = CGSize(width: 44, height: 14)
self.addChild(cloud)
//pass the SKSpriteNode into the moveNode function
self.moveNode(cloud)
}
//reposition the node
func repositionNode(node: SKSpriteNode) {
print("repositioning node")
node.position = CGPointMake(self.frame.size.width - 100, self.randomCloudPosition())
print(node.position.y)
self.moveNode(node)
}
//move the node again
func moveNode(node: SKSpriteNode) {
node.runAction(SKAction.moveToX(-100, duration: 28), completion: {
self.repositionNode(node)
})
}
//return a random number between 300 and 799 (change this for your specific case)
func randomCloudPosition() -> CGFloat {
return CGFloat(arc4random_uniform(500) + 300) //should be between 300 and 799
}
}
You should be able to create your five clouds (or however many) and then just pass each one into moveNode and the animation will continue indefinitely

Building my first game in XCODE 7(swift 2)-CGPOINTMAKE?

I want to build a game similar to snooker, and at the beginning I already have some problems. I want to build four walls first (that will be the size of the screen-self.frame), and the first one I made like this:
let ground = SKNode()
ground.position = CGPointMake ( 0, 0)
ground.physicsBody = SKPhysicsBody( rectangleOfSize:CGSizeMake(self.frame.size.width * 3, 1))
ground.physicsBody!.dynamic = false
self.addChild(ground)
However, I don't known how to manipulate the values of CGPointMake to do the left/right/up walls. I first imagined that the (0,0) point was the left down corner, but it seems to not be like that. Can someone please help me with this or just explain how this works? (since in https://developer.apple.com/library/prerelease/ios/documentation/GraphicsImaging/Reference/CGGeometry/index.html#//apple_ref/c/func/CGPointMake they dont explain very much =/)
Here is a basic example of how you can restrict the ball from leaving the screen:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let BallCategory : UInt32 = 0x1 << 1
let WallCategory : UInt32 = 0x1 << 2
let ball = SKShapeNode(circleOfRadius: 40)
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsWorld.contactDelegate = self
setupWalls()
setupBall()
}
func setupWalls(){
physicsBody = SKPhysicsBody(edgeLoopFromRect: frame)
physicsBody?.categoryBitMask = WallCategory
physicsBody?.contactTestBitMask = BallCategory
physicsBody?.collisionBitMask = BallCategory
}
func setupBall(){
ball.physicsBody = SKPhysicsBody(circleOfRadius: 40)
ball.fillColor = SKColor.whiteColor()
ball.name = "ball"
ball.physicsBody?.categoryBitMask = BallCategory
ball.physicsBody?.contactTestBitMask = WallCategory
ball.physicsBody?.collisionBitMask = WallCategory
ball.physicsBody?.dynamic = true //In order to detect contact between two bodies, at least on body has to be dynamic
ball.physicsBody?.affectedByGravity = false
ball.physicsBody?.restitution = 0.5
ball.position = CGPoint(x: CGRectGetMidX(frame), y: CGRectGetMidY(frame)) // placing to ball in the middle of the screen
addChild(ball)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
ball.physicsBody?.applyImpulse(CGVector(dx: 200, dy: 200))
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Try this for adding four walls with a thickness of twenty:
// Create walls
let leftWall = SKShapeNode(rect: CGRect(x: 0, y: 0, width: 20, height: self.frame.height))
leftWall.fillColor = UIColor.whiteColor()
let physicsBodyLW = SKPhysicsBody(rectangleOfSize: CGSize(width: 20, height: self.frame.size.height), center: CGPoint(x:10, y:self.frame.height/2))
physicsBodyLW.affectedByGravity = false
physicsBodyLW.dynamic = false
leftWall.physicsBody = physicsBodyLW
self.addChild(leftWall)
let rightWall = SKShapeNode(rect: CGRect(x: self.frame.width - 20, y: 0, width: 20, height: self.frame.height))
rightWall.fillColor = UIColor.whiteColor()
let physicsBodyRW = SKPhysicsBody(rectangleOfSize: CGSize(width: 20, height: self.frame.size.height), center: CGPoint(x:10, y:self.frame.height/2))
physicsBodyRW.affectedByGravity = false
physicsBodyRW.dynamic = false
rightWall.physicsBody = physicsBodyRW
self.addChild(rightWall)
let topWall = SKShapeNode(rect: CGRect(x: 0, y: self.frame.height-20, width: self.frame.width, height: 20))
topWall.fillColor = UIColor.whiteColor()
let physicsBodyTW = SKPhysicsBody(rectangleOfSize: CGSize(width: self.frame.size.width, height: 20), center: CGPoint(x:self.frame.width/2, y:10))
physicsBodyTW.affectedByGravity = false
physicsBodyTW.dynamic = false
topWall.physicsBody = physicsBodyTW
self.addChild(topWall)
let bottomWall = SKShapeNode(rect: CGRect(x: 0, y: 0, width: self.frame.width, height: 20))
print(bottomWall.frame)
bottomWall.fillColor = UIColor.whiteColor()
let physicsBodyBW = SKPhysicsBody(rectangleOfSize: CGSize(width: self.frame.size.width, height: 20), center: CGPoint(x:self.frame.width/2, y:10))
physicsBodyBW.affectedByGravity = false
physicsBodyBW.dynamic = false
bottomWall.physicsBody = physicsBodyBW
self.addChild(bottomWall)

Resources