Why does the app lag a lot when calling functions - swift

I am building a game that requires a function (out of several) to be called depending if a condition is true or not. The condition checks the variable every 0.0001 seconds for game purposes, but the functions are called every 4-6 seconds. I just needed the
Whenever I was testing the game on a device, I noticed that the app lags at certain times. I believe its when a function is called but I'm not sure.
This is an SKAction that calls the function that checks the condition:
let triangle = SKAction.sequence([SKAction.run(obstaclesFunc),SKAction.wait(forDuration: 0.0001)])
run(SKAction.repeatForever(triangle))
This is the condition checker:
func obstaclesFunc() {
GameScene.nextObstacle = Int(arc4random_uniform(6) + 1 )
if(GameScene.first == true){
GameScene.randomNum = 1
}
if (GameScene.pass == true){
number = GameScene.nextObstacle
print(number)
//Rectangle Call Function
if(GameScene.randomNum == 1){
sideRectangles()
//More code at bottom
}
I noticed that the biggest lag occurs when this function is called:
func sideRectangles() {
let rectangle1 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle2 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle3 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangleTexture = SKTexture(imageNamed: "rectangleWalls")
//Physics World
rectangle1.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle1.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_1
rectangle1.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle1.physicsBody?.affectedByGravity = false
rectangle1.physicsBody?.isDynamic = true
rectangle1.physicsBody?.collisionBitMask = 0
//Physics World
rectangle2.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle2.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_2
rectangle2.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle2.physicsBody?.affectedByGravity = false
rectangle2.physicsBody?.isDynamic = true
rectangle2.physicsBody?.collisionBitMask = 0
//Physics World
rectangle3.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle3.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_3
rectangle3.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle3.physicsBody?.affectedByGravity = false
rectangle3.physicsBody?.isDynamic = true
rectangle3.physicsBody?.collisionBitMask = 0
let moveDown = SKAction.moveTo(y: self.frame.minY - rectangle3.size.height / 2, duration: 2)
let moveDown2 = SKAction.moveTo(y: self.frame.minY - rectangle3.size.height / 2, duration: 3)
let moveDown3 = SKAction.moveTo(y: self.frame.minY - rectangle3.size.height / 2, duration: 4)
let removeNode = SKAction.removeFromParent()
rectangle1.position = CGPoint(x: self.frame.minX - rectangle3.size.height * 0.1, y: self.frame.maxY + rectangle3.size.width) //use 4.5 for the close one
rectangle2.position = CGPoint(x: self.frame.minX - rectangle3.size.width / 40 , y: self.frame.maxY + rectangle3.size.height) //use 4.5 for the close one
rectangle3.position = CGPoint(x: self.frame.midX - rectangle3.size.width / 1.55, y: self.frame.maxY + rectangle3.size.height * 1.8)
rectangle1.run(SKAction.sequence([moveDown,removeNode]))
rectangle2.run(SKAction.sequence([moveDown2,removeNode]))
rectangle3.run(SKAction.sequence([moveDown3,removeNode]))
//second set
let rectangle5 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle6 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle7 = SKSpriteNode(imageNamed: "rectangleWalls")
//Physics World
rectangle5.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle5.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_5
rectangle5.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle5.physicsBody?.affectedByGravity = false
rectangle5.physicsBody?.isDynamic = true
rectangle5.physicsBody?.collisionBitMask = 0
//Physics World
rectangle6.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle6.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_6
rectangle6.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle6.physicsBody?.affectedByGravity = false
rectangle6.physicsBody?.isDynamic = true
rectangle6.physicsBody?.collisionBitMask = 0
//Physics World
rectangle7.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle7.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_7
rectangle7.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle7.physicsBody?.affectedByGravity = false
rectangle7.physicsBody?.isDynamic = true
rectangle7.physicsBody?.collisionBitMask = 0
rectangle5.zPosition = 0
rectangle6.zPosition = 0
rectangle7.zPosition = 0
rectangle5.position = CGPoint(x: self.frame.maxX + rectangle3.size.height * 0.1, y: self.frame.maxY + rectangle3.size.width) //use 4.5 for the close one
rectangle6.position = CGPoint(x: self.frame.maxX + rectangle3.size.width / 40 , y: self.frame.maxY + rectangle3.size.height) //use 4.5 for the close one
rectangle7.position = CGPoint(x: self.frame.midX + rectangle3.size.width / 1.55, y: self.frame.maxY + rectangle3.size.height * 1.8)
rectangle5.run(SKAction.sequence([moveDown,removeNode]))
rectangle6.run(SKAction.sequence([moveDown2,removeNode]))
rectangle7.run(SKAction.sequence([moveDown3,removeNode]))
addChild(rectangle1)
addChild(rectangle2)
addChild(rectangle3)
addChild(rectangle5)
addChild(rectangle6)
addChild(rectangle7)
}
Can someone please help me find out what is causing this lag? I cannot seem to figure it out. Thank You in advance!

Generating physicsBodies on the fly is very expensive and that is why your game will lag everytime it tries to do that.
I suggest you create the objects and put them in an array of the obstacle and then copy the array items whenever you need to recreate them.
You have soooooo much code that is being repeated for every object, I've tried to reduce the amount of code by reusing a lot of it for every obstacle.
FYI SKAction.wait(forDuration: 0.0001) is the same as 0 so it is pointless
The code in your obstaclesFunc contains irrelevant or code not contained in your questions so I have omitted that functionality, if it is dire to your game you'll have to figure it out and put it in the generateObstacles func.
//variables needed in your scene
private var rectangles = [SKSpriteNode]()
private var moveDown: SKAction!
private var moveDown2: SKAction!
private var moveDown3: SKAction!
private var removeNode: SKAction!
//in your didMove func
//preload the obstacles into an array
createRectangles()
//in your startGame func
//this func call will start the generation of the obstacles
generateObstacles()
func createRectangles() {
let rectangle1 = createRectangle(category: PhysicsNumbering.rectangle_1)
rectangles.append(rectangle1)
let rectangle2 = createRectangle(category: PhysicsNumbering.rectangle_2)
rectangles.append(rectangle2)
let rectangle3 = createRectangle(category: PhysicsNumbering.rectangle_3)
rectangles.append(rectangle3)
let rectangle5 = createRectangle(category: PhysicsNumbering.rectangle_5)
rectangles.append(rectangle5)
let rectangle6 = createRectangle(category: PhysicsNumbering.rectangle_6)
rectangles.append(rectangle6)
let rectangle7 = createRectangle(category: PhysicsNumbering.rectangle_7)
rectangles.append(rectangle7)
let width: CGFloat = rectangle3.size.width
let height: CGFloat = rectangle3.size.height
//position the rectangles afterward creating them since they appear to be relevant to each other
rectangle1.position = CGPoint(x: frame.minX - height * 0.1, y: frame.maxY + width) //use 4.5 for the close one
rectangle2.position = CGPoint(x: frame.minX - width / 40 , y: frame.maxY + height) //use 4.5 for the close one
rectangle3.position = CGPoint(x: frame.midX - width / 1.55, y: frame.maxY + height * 1.8)
rectangle5.position = CGPoint(x: frame.maxX + height * 0.1, y: frame.maxY + width) //use 4.5 for the close one
rectangle6.position = CGPoint(x: frame.maxX + width / 40 , y: frame.maxY + height) //use 4.5 for the close one
rectangle7.position = CGPoint(x: frame.midX + width / 1.55, y: frame.maxY + height * 1.8)
//now create the actions so that they can be used again and again
createActions(height: height / 2)
}
func createActions(height: CGFloat) {
moveDown = SKAction.moveTo(y: frame.minY - height, duration: 2)
moveDown2 = SKAction.moveTo(y: frame.minY - height, duration: 3)
moveDown3 = SKAction.moveTo(y: frame.minY - height, duration: 4)
removeNode = SKAction.removeFromParent()
}
func generateObstacles() {
let randomWaitTime = Int(arc4random_uniform(6) + 1 )
self.run(.wait(forDuration: randomWaitTime) {
loadAndMoveRectangles()
//now call the generate func again to get a diff random time to wait
generateObstacles()
}
}
func loadAndMoveRectangles() {
let rectangle1 = rectangles[0].copy() as? SKSpriteNode
addChild(rectangle1)
let rectangle2 = rectangles[1].copy() as? SKSpriteNode
addChild(rectangle2)
let rectangle3 = rectangles[2].copy() as? SKSpriteNode
addChild(rectangle3)
let rectangle5 = rectangles[3].copy() as? SKSpriteNode
addChild(rectangle5)
let rectangle6 = rectangles[4].copy() as? SKSpriteNode
addChild(rectangle6)
let rectangle7 = rectangles[5].copy() as? SKSpriteNode
addChild(rectangle7)
rectangle1.run(.sequence([moveDown, removeNode]))
rectangle2.run(.sequence([moveDown2, removeNode]))
rectangle3.run(.sequence([moveDown3, removeNode]))
rectangle5.run(.sequence([moveDown, removeNode]))
rectangle6.run(.sequence([moveDown2, removeNode]))
rectangle7.run(.sequence([moveDown3, removeNode]))
}
func createRectangle(category: UInt32) -> SKSpriteNode {
let rectangle = SKSpriteNode(imageNamed: "rectangleWalls")
rectangle.zPosition = 0
rectangle.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle.physicsBody?.categoryBitMask = category
rectangle.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle.physicsBody?.affectedByGravity = false
rectangle.physicsBody?.isDynamic = true
rectangle.physicsBody?.collisionBitMask = 0
return rectangle
}

Related

Node spacing issues on a vertical scrolling game with variable speeds in Spritekit

So I'm creating a game in Swift with Spritekit and have ran into an issue with getting my game to work. I'm still a beginner with programming so I've likely missed out on a solution to this myself.
Anyway, so the game concept is a simple arcade vertical scroller that involves a player trying to dodge platforms as it descends downward. The mechanics (so far) are a stationary player on the y axis that can move left and right along the x axis while the platforms scroll upward along with the background moving with the platforms to give a visual effect of descent. I've gotten a build working to be fully playable, but there's an issue with spawning the platforms perfectly spaced out. Here's a sketch:
Concept Image
The picture on the left what I'm trying to achieve, while the one on the right is my current and flawed method. The main issue with the one on the right, is that it uses a collision to trigger spawning which means the spawn trigger node (red line) has to be 1 pixel tall to allow for perfect spacing. If the spawn trigger node is more than 1 pixel tall, then the collision may not trigger on that the first pixel of contact and trigger the node a few pixels deep which throws off the entire spacing. Also if the spawn trigger is only 1 pixel tall, it often won't trigger unless the everything is scrolling at slow speeds.
I've tried to think of other methods to approach this but I'm at a loss. I cannot use a simple timer to spawn nodes at intervals because the speed at which the game scrolls is variable and is constantly changing by player controls. The only two other options I can think of (which I don't know how to do either) is either spawn node sets at fixed y-positions and set that on a loop, or change it so the player is actually descending downward while everything is generating around it (seems tougher and maybe unnecessary). I'm considering just rewriting my createPlatforms() method if I need to, but here's the code for that and the background anyway:
var platformGroup = Set<SKSpriteNode>()
var platformSpeed: CGFloat = 0.6 { didSet { for platforms in platformGroup { platforms.speed = platformSpeed } } }
var platformTexture: SKTexture!
var platformPhysics: SKPhysicsBody!
var platformCount = 0
var backgroundPieces: [SKSpriteNode] = [SKSpriteNode(), SKSpriteNode()]
var backgroundSpeed: CGFloat = 1.0 { didSet { for background in backgroundPieces { background.speed = backgroundSpeed } } }
var backgroundTexture: SKTexture! { didSet { for background in backgroundPieces { background.texture = backgroundTexture } } }
func createPlatforms() {
let min = CGFloat(frame.width / 12)
let max = CGFloat(frame.width / 3)
var xPosition = CGFloat.random(in: -min ... max)
if platformCount >= 20 && platformCount < 30 {
stage = 0
setTextures()
xPosition = frame.size.width * 0.125
} else if platformCount == 30 {
stage = 2
setTextures()
} else if platformCount >= 50 && platformCount < 60 {
stage = 0
setTextures()
xPosition = 184
} else if platformCount == 60 {
stage = 3
setTextures()
}
platformPhysics = SKPhysicsBody(rectangleOf: CGSize(width: platformTexture.size().width, height: platformTexture.size().height))
let platformLeft = SKSpriteNode(texture: platformTexture)
platformLeft.physicsBody = platformPhysics.copy() as? SKPhysicsBody
platformLeft.physicsBody?.isDynamic = false
platformLeft.physicsBody?.affectedByGravity = false
platformLeft.physicsBody?.collisionBitMask = 0
platformLeft.scale(to: CGSize(width: platformLeft.size.width * 3, height: platformLeft.size.height * 3))
platformLeft.zPosition = 20
platformLeft.name = "platform"
platformLeft.speed = platformSpeed
let platformRight = SKSpriteNode(texture: platformTexture)
platformRight.physicsBody = platformPhysics.copy() as? SKPhysicsBody
platformRight.physicsBody?.isDynamic = true
platformRight.physicsBody?.collisionBitMask = 0
platformRight.scale(to: CGSize(width: platformRight.size.width * 3, height: platformRight.size.height * 3))
platformRight.zPosition = 20
platformRight.name = "platform"
platformRight.speed = platformSpeed
let scoreNode = SKSpriteNode(color: UIColor.clear, size: CGSize(width: frame.width, height: 32))
scoreNode.physicsBody = SKPhysicsBody(rectangleOf: scoreNode.size)
scoreNode.physicsBody?.isDynamic = false
scoreNode.zPosition = 100
scoreNode.name = "scoreDetect"
scoreNode.speed = platformSpeed
let platformTrigger = SKSpriteNode(color: UIColor.orange, size: CGSize(width: frame.width, height: 4))
platformTrigger.physicsBody = SKPhysicsBody(rectangleOf: platformTrigger.size)
platformTrigger.physicsBody?.isDynamic = true
platformTrigger.physicsBody?.affectedByGravity = false
platformTrigger.physicsBody?.categoryBitMask = Collisions.detect
platformTrigger.physicsBody?.contactTestBitMask = Collisions.spawn
platformTrigger.physicsBody?.collisionBitMask = 0
platformTrigger.physicsBody?.usesPreciseCollisionDetection = true
platformTrigger.zPosition = 100
platformTrigger.name = "platformTrigger"
platformTrigger.speed = platformSpeed
let newNodes: Set<SKSpriteNode> = [platformLeft, platformRight, scoreNode, platformTrigger]
for node in newNodes {
platformGroup.insert(node)
}
let yPosition = spawnNode.position.y - transitionPlatform.size().height
let gapSize: CGFloat = -frame.size.width / 6
print(gapSize)
platformLeft.position = CGPoint(x: xPosition + platformLeft.size.width - gapSize, y: -yPosition)
platformRight.position = CGPoint(x: xPosition + gapSize, y: -yPosition)
scoreNode.position = CGPoint(x: frame.midX, y: platformLeft.position.y - platformLeft.size.height / 2)
platformTrigger.position = CGPoint(x: frame.midX, y: platformLeft.position.y)
print(platformLeft.position.y)
print(platformLeft.frame.midY)
let endPosition = frame.maxY + frame.midY
let moveAction = SKAction.moveBy(x: 0, y: endPosition, duration: 7)
for node in newNodes {
let moveSequence = SKAction.sequence([
moveAction,
SKAction.removeFromParent(),
SKAction.run {
self.platformGroup.remove(node)
}
])
addChild(node)
nodeArray.append(node)
node.run(moveSequence)
}
platformCount += 1
}
func startPlatforms() {
let create = SKAction.run { [unowned self] in
self.createPlatforms()
}
run(create)
}
func createBackground() {
for i in 0 ... 1 {
let background = backgroundPieces[i]
background.texture = backgroundTexture
background.anchorPoint = CGPoint(x: 0, y: 0)
background.zPosition = -5
background.size = CGSize(width: frame.size.width, height: frame.size.width * 2.5)
background.position = CGPoint(x: 0, y: background.size.height + (-background.size.height) + (-background.size.height * CGFloat(i)))
self.addChild(background)
nodeArray.append(background)
let scrollUp = SKAction.moveBy(x: 0, y: background.size.height, duration: 5)
let scrollReset = SKAction.moveBy(x: 0, y: -background.size.height, duration: 0)
let scrollLoop = SKAction.sequence([scrollUp, scrollReset])
let scrollForever = SKAction.repeatForever(scrollLoop)
background.run(scrollForever)
}
}
Does anybody have any suggestions on how I approach this or change it so it would work perfectly everytime?

Getting SpriteKit texture from SKView returning nil after 104 calls

I am having some odd behavior in SpriteKit when creating a texture. The function below shows you what I am doing. In short, I'm in SceneKit and making a SCNNode out of an Array of colors (think pixel/voxels). It works like a charm. However, after exactly 104 calls the texture returned is nil. Afterwards, it is hit or miss whether the texture will be nil or not. I am also providing the exact color information. Thoughts?
func create2dModelSK(with colors: [String]) -> SCNNode? {
let geo = SCNBox(width: 1.0, height: 1.0, length: 0.1, chamferRadius: 0.0)
let base = SCNNode(geometry: geo)
let view = SKSpriteNode(color: .white, size: CGSize(width: 160, height: 160))
view.anchorPoint = CGPoint(x: 0, y: 1)
var xOffset = 0
var yOffset = 0
var count = 0
for _ in 0...15 {
for _ in 0...15 {
guard let newColor = UIColor(hexString: "#" + colors[count] + "ff") else { return base }
let n = SKSpriteNode(color: newColor, size: CGSize(width: 10, height: 10))
n.anchorPoint = CGPoint(x: 0, y: 1)
n.position = CGPoint(x: xOffset, y: yOffset)
view.addChild(n)
xOffset += 10
count += 1
}
xOffset = 0
yOffset -= 10
}
let skView = SKView(frame: CGRect(x: 0, y: 0, width: 160, height: 160))
let texture = skView.texture(from: view)
//AFTER being called 104 times, texture is nil.
let faceMaterial = SCNMaterial()
faceMaterial.diffuse.contents = texture
let sideMaterial = SCNMaterial()
sideMaterial.diffuse.contents = UIColor.white
let materialsForBox = [faceMaterial,sideMaterial,faceMaterial,sideMaterial,sideMaterial,sideMaterial]
base.geometry?.materials = materialsForBox
let scale = SCNVector3(x: 0.1, y: 0.1, z: 0.1)
base.scale = scale
return base
}
This is where autoreleasepool comes in handy, it allows you to release the memory when the autoreleasepool is finished so that you do not run out of space before using it again.
Of course this is not going to solve your main problem, where you are creating too many textures and running out of memory space, but it will allow you to at least make some more because it will release the temporary memory that view.texture(from:node) is holding on to.
func create2dModelSK(with colors: [String]) -> SCNNode? {
let geo = SCNBox(width: 1.0, height: 1.0, length: 0.1, chamferRadius: 0.0)
let base = SCNNode(geometry: geo)
let view = SKSpriteNode(color: .white, size: CGSize(width: 160, height: 160))
view.anchorPoint = CGPoint(x: 0, y: 1)
var xOffset = 0
var yOffset = 0
var count = 0
for _ in 0...15 {
for _ in 0...15 {
guard let newColor = UIColor(hexString: "#" + colors[count] + "ff") else { return base }
let n = SKSpriteNode(color: newColor, size: CGSize(width: 10, height: 10))
n.anchorPoint = CGPoint(x: 0, y: 1)
n.position = CGPoint(x: xOffset, y: yOffset)
view.addChild(n)
xOffset += 10
count += 1
}
xOffset = 0
yOffset -= 10
}
autoreleasepool{
let skView = SKView(frame: CGRect(x: 0, y: 0, width: 160, height: 160))
let texture = skView.texture(from: view)
//AFTER being called 104 times, texture is nil.
let faceMaterial = SCNMaterial()
faceMaterial.diffuse.contents = texture
let sideMaterial = SCNMaterial()
sideMaterial.diffuse.contents = UIColor.white
let materialsForBox = [faceMaterial,sideMaterial,faceMaterial,sideMaterial,sideMaterial,sideMaterial]
base.geometry?.materials = materialsForBox
}
let scale = SCNVector3(x: 0.1, y: 0.1, z: 0.1)
base.scale = scale
return base
}

How to have two SKSpriteNodes always have the same position?

I have two SKSpriteNodes that I would like to keep the same position. newLeftObject, and newLeftObjectBackground. What is the best way to make it so that the two objects always keep the same position even when force is applied to them. I tried setting their mass and density equal to each other but they still end up having slightly different positions while falling. Thank you, any help is appreciated!
func createNewObjectLeftAndMoveToCenter() {
colorBackground.removeFromParent()
blackBackground.removeFromParent()
var newLeftObject = SKSpriteNode()
var newRightObject = SKSpriteNode()
var newLeftObjectBackground = SKSpriteNode()
var newRightObjectBackground = SKSpriteNode()
var distance = sqrt(((colorBackground.size.width / -2) - (verticalTarget.position.x)) * ((colorBackground.size.width / -2) - (verticalTarget.position.x)))
var distanceToCenter = sqrt(((0 - (verticalTarget.position.x))) * (0 - (verticalTarget.position.x)))
newLeftObject.size = CGSize(width: distance, height: colorBackground.size.height)
newLeftObject.position = CGPoint(x: verticalTarget.position.x - (newLeftObject.size.width / 2), y: colorBackground.position.y)
newLeftObject.color = colorBackground.color
newLeftObject.zPosition = 30
addChild(newLeftObject)
newLeftObjectBackground.size = CGSize(width: distance + 14, height: blackBackground.size.height)
newLeftObjectBackground.position = CGPoint(x: verticalTarget.position.x - (newLeftObject.size.width / 2), y: colorBackground.position.y)
newLeftObjectBackground.color = UIColor.black
newLeftObjectBackground.zPosition = 20
addChild(newLeftObjectBackground)
newRightObject.size = CGSize(width: distanceToCenter + colorBackground.size.width / 2, height: colorBackground.size.height)
newRightObject.position = CGPoint(x: verticalTarget.position.x + (newRightObject.size.width / 2), y: colorBackground.position.y)
newRightObject.color = colorBackground.color
newRightObject.zPosition = 40
addChild(newRightObject)
newRightObjectBackground.size = CGSize(width: (distanceToCenter + colorBackground.size.width / 2) + 14, height: blackBackground.size.height)
newRightObjectBackground.position = CGPoint(x: verticalTarget.position.x + (newRightObject.size.width / 2), y: colorBackground.position.y)
newRightObjectBackground.color = blackBackground.color
newRightObjectBackground.zPosition = 35
addChild(newRightObjectBackground)
newLeftObject.physicsBody = SKPhysicsBody(rectangleOf: newLeftObject.size)
newLeftObject.physicsBody?.isDynamic = true
newLeftObject.physicsBody?.affectedByGravity = true
newLeftObject.physicsBody?.allowsRotation = true
newLeftObject.physicsBody?.collisionBitMask = 0
newLeftObject.physicsBody?.density = 1
newLeftObject.physicsBody?.mass = 1
newLeftObject.physicsBody?.applyAngularImpulse(1)
newLeftObject.physicsBody?.applyForce(CGVector(dx: -10000, dy: 10000))
newLeftObjectBackground.physicsBody = SKPhysicsBody(rectangleOf: newLeftObjectBackground.size)
newLeftObjectBackground.physicsBody?.isDynamic = true
newLeftObjectBackground.physicsBody?.affectedByGravity = true
newLeftObjectBackground.physicsBody?.collisionBitMask = 0
newLeftObjectBackground.physicsBody?.density = 1
newLeftObjectBackground.physicsBody?.mass = 1
newLeftObjectBackground.physicsBody?.allowsRotation = true
newLeftObjectBackground.physicsBody?.applyAngularImpulse(1)
newLeftObjectBackground.physicsBody?.applyForce(CGVector(dx: -10000, dy: 10000))
newLeftObject.position = newLeftObjectBackground.position
var moveNewObjectToCenter = SKAction.moveTo(x: 0, duration: 0.25)
newRightObject.run(moveNewObjectToCenter)
newRightObjectBackground.run(moveNewObjectToCenter)
colorBackground = newRightObject
blackBackground = newRightObjectBackground
}
Make one a child of the other using addChild()

Randomly spawn SKNode on the x position?

I have a game and I can't seem to get the node to randomly spawn on x position, it will randomly spawn but often will go off screen and the game is then useless and have to restart it:
func addEnemy () {
//enemy
let minValue = self.size.width / 8;
let maxValue = self.size.width-20;
let spawnPoint = UInt32(maxValue - minValue);
Enemy = SKSpriteNode(imageNamed: "Enemy")
Enemy.size = CGSize(width: 150, height: 200)
Enemy.position = CGPoint(x: CGFloat(arc4random_uniform(spawnPoint)), y: self.size.height)
self.addChild(Enemy)
}
let min: CGFloat = 15.0
let max: CGFloat = 200.0
let randomCGFloatBetweenMinAndMax = CGFloat(rand())/CGFloat(RAND_MAX)*(max-min)+min
or if you prefer
let randomCGFloatBetweenMinAndMax2 = CGFloat(arc4random_uniform(UInt32(max-min)) + UInt32(min))

Swift game infinite spawning not working

i've a problem with my swift game. I want to spawn every 2 seconds two sprites and move them like flappy bird pipes. I tried on this way but my game crashes after the delay. Here is DidMoveToView: ( i jump right on the interesting parts)
let distanceToMove = CGFloat(self.frame.size.width + 140)
let movePipes = SKAction.repeatActionForever(SKAction.moveByX(-distanceToMove, y: 0, duration: NSTimeInterval(0.01 * distanceToMove)))
let removePipes = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([movePipes,removePipes])
let delay = SKAction.waitForDuration(NSTimeInterval(2.0))
let spawn = SKAction.runBlock({() in self.initPipes()})
let spawnAndDelay = SKAction.sequence([spawn,delay])
let spawnAndDelayForever = SKAction.repeatActionForever(spawnAndDelay)
runAction(spawnAndDelayForever)
And here is the func of the pipes:
func initPipes() {
let pY = arc4random_uniform(UInt32(self.size.height - 250) + 250)
let pipePair = SKNode()
pipePair.position = CGPoint(x: self.frame.size.width + 70, y: 0)
//PIPE 1
pipe1.anchorPoint = CGPointMake(0, 0)
pipe1.position = CGPoint(x: 0, y: Int(pY))
pipe1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(70, 700), center: CGPointMake(70/2, 700/2))
pipe1.physicsBody?.dynamic = false
pipe1.physicsBody?.affectedByGravity = false
pipePair.addChild(pipe1)
//PIPE 2
pipe2.anchorPoint = CGPointMake(0,1)
pipe2.position = CGPoint(x: 0, y: pipe1.position.y - 150)
pipe2.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(70, 700), center: CGPointMake(35, -700/2))
pipe2.physicsBody?.dynamic = false
pipe2.physicsBody?.affectedByGravity = false
pipePair.addChild(pipe2)
pipePair.runAction(moveAndRemove)
addChild(pipePair)
}
Any idea? Thanks
I think your issue is here
pipePair.addChild(pipe1)
You are not creating a new pipe but instead using an old one you created somewhere else. The first time through you are not going to have a problem, but if you didn't remove the pipe1 from its parent before you try adding it to a new parent it will crash. I would create your pipe1 and pipe2 in that method instead of having a variable outside of it.
func initPipes() {
let pY = arc4random_uniform(UInt32(self.size.height - 250) + 250)
let pipePair = SKNode()
pipePair.position = CGPoint(x: self.frame.size.width + 70, y: 0)
//PIPE 1
let pipe1 = //whatever pipe1 is
pipe1.anchorPoint = CGPointMake(0, 0)
pipe1.position = CGPoint(x: 0, y: Int(pY))
pipe1.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(70, 700), center: CGPointMake(70/2, 700/2))
pipe1.physicsBody?.dynamic = false
pipe1.physicsBody?.affectedByGravity = false
pipePair.addChild(pipe1)
//PIPE 2
let pipe2 = //whatever pipe1 is
pipe2.anchorPoint = CGPointMake(0,1)
pipe2.position = CGPoint(x: 0, y: pipe1.position.y - 150)
pipe2.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(70, 700), center: CGPointMake(35, -700/2))
pipe2.physicsBody?.dynamic = false
pipe2.physicsBody?.affectedByGravity = false
pipePair.addChild(pipe2)
pipePair.runAction(moveAndRemove)
addChild(pipePair)
}
or if the intention is to only have one set of pipes at a time call
pipe1.removeFromParent()
pipe2.removeFromParent()
before
pipePair.addChild(pipe1)
pipePair.addChild(pipe2)
Hopefully that makes sense and is helpful.