Zig zac path in sprite kit - swift

I want the sprite kit node to move 200 px to the right and 400px to left and then again 400px to right and so on until leaves the screen
var cgpath: CGMutablePathRef = CGPathCreateMutable();
// random values
var xStart: CGFloat = CGFloat(0.0);
var xEnd: CGFloat = CGFloat(0.0);
// ControlPoint1
var cp1X: CGFloat = CGFloat(200.0);
var cp1Y: CGFloat = CGFloat(400.0);
// ControlPoint2
var cp2X: CGFloat = CGFloat(200.0);
var cp2Y: CGFloat = CGFloat(280.0);
var s: CGPoint = CGPointMake(xStart,-self.frame.size.height/2);
var e: CGPoint = CGPointMake(xEnd, self.frame.size.height/2);
var cp1: CGPoint = CGPointMake(cp1X, cp1Y);
var cp2: CGPoint = CGPointMake(cp2X, cp2Y);
CGPathMoveToPoint(cgpath,nil, s.x, s.y);
CGPathAddCurveToPoint(cgpath, nil, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
var ball:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(40, 40));
var followTrack = SKAction.followPath(cgpath, asOffset: false, orientToPath: true, duration: 15.0);
var forever: SKAction = SKAction.repeatActionForever(followTrack);
ball.runAction(forever);
self.addChild(ball);
let shapeNode = SKShapeNode(path: cgpath)
self.addChild(shapeNode);

Try the following code to get a zig zag path using bezier curves.
var cgpath: CGMutablePathRef = CGPathCreateMutable();
var xStart: CGFloat = CGFloat(200.0);
var xEnd: CGFloat = CGFloat(200.0);
var yStart : CGFloat = 0
var yEnd : CGFloat = self.size.height
let numberOfIntermediatePoints = 10
let xOffset : CGFloat = 400
let intervalY = (yEnd - yStart)/CGFloat(numberOfIntermediatePoints)
var s: CGPoint = CGPointMake(xStart,yStart);
CGPathMoveToPoint(cgpath,nil, s.x, s.y);
for i in stride(from: 0, through: numberOfIntermediatePoints - 1, by: 1) {
let yOff = intervalY * CGFloat(i) + yStart
let controlPointInterval = intervalY/3
var cp1X: CGFloat = CGFloat(xStart + xOffset);
var cp1Y: CGFloat = CGFloat(yStart + yOff + controlPointInterval);
var cp2X: CGFloat = CGFloat(xStart - xOffset);
var cp2Y: CGFloat = CGFloat(yStart + yOff + controlPointInterval * 2);
var e: CGPoint = CGPointMake(xEnd, yStart + yOff + intervalY);
var cp1: CGPoint = CGPointMake(cp1X, cp1Y);
var cp2: CGPoint = CGPointMake(cp2X, cp2Y);
CGPathAddCurveToPoint(cgpath, nil, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
}
var ball:SKSpriteNode = SKSpriteNode(color: UIColor.redColor(), size: CGSizeMake(40, 40));
var followTrack = SKAction.followPath(cgpath, asOffset: false, orientToPath: true, duration: 5.0);
var forever: SKAction = SKAction.repeatActionForever(followTrack);
ball.runAction(forever);
self.addChild(ball);
let shapeNode = SKShapeNode(path: cgpath)
self.addChild(shapeNode);
Change numberOfIntermediatePoints and xOffset values to change the number of zig zags and the amount of distance moved in each zig zag.

Related

how to shorten grid game in swift

I am making a grid game in swift where there is a five by five grid of squares and they each are a skspritenode and when the player moves on them they turn to a different color.
This is what i have done so far
import SpriteKit
class GameScene: SKScene {
var alive = Bool()
var targetColors = Array<UIColor>()
var permanent = Array<Bool>()
var has3Colors = Array<Bool>()
var secondColors = Array<UIColor>()
var positions = Array<CGPoint>()
var startColors = Array<UIColor>()
var circle = SKSpriteNode()
var completedSquares = Int()
var started = Bool()
var squares = Array<SKSpriteNode>()
var square1 = SKSpriteNode(imageNamed: "SquareTile")
var square2 = SKSpriteNode(imageNamed: "SquareTile")
var square3 = SKSpriteNode(imageNamed: "SquareTile")
var square4 = SKSpriteNode(imageNamed: "SquareTile")
var square5 = SKSpriteNode(imageNamed: "SquareTile")
var square6 = SKSpriteNode(imageNamed: "SquareTile")
var square7 = SKSpriteNode(imageNamed: "SquareTile")
var square8 = SKSpriteNode(imageNamed: "SquareTile")
var square9 = SKSpriteNode(imageNamed: "SquareTile")
var square10 = SKSpriteNode(imageNamed: "SquareTile")
var square11 = SKSpriteNode(imageNamed: "SquareTile")
var square12 = SKSpriteNode(imageNamed: "SquareTile")
var square13 = SKSpriteNode(imageNamed: "SquareTile")
var square14 = SKSpriteNode(imageNamed: "SquareTile")
var square15 = SKSpriteNode(imageNamed: "SquareTile")
var square16 = SKSpriteNode(imageNamed: "SquareTile")
var square17 = SKSpriteNode(imageNamed: "SquareTile")
var square18 = SKSpriteNode(imageNamed: "SquareTile")
var square19 = SKSpriteNode(imageNamed: "SquareTile")
var square20 = SKSpriteNode(imageNamed: "SquareTile")
var square21 = SKSpriteNode(imageNamed: "SquareTile")
var square22 = SKSpriteNode(imageNamed: "SquareTile")
var square23 = SKSpriteNode(imageNamed: "SquareTile")
var square24 = SKSpriteNode(imageNamed: "SquareTile")
var square25 = SKSpriteNode(imageNamed: "SquareTile")
var basicSize = CGSize()
var columb1 = CGFloat()
var columb2 = CGFloat()
var columb3 = CGFloat()
var columb4 = CGFloat()
var columb5 = CGFloat()
var row1 = CGFloat()
var row2 = CGFloat()
var row3 = CGFloat()
var row4 = CGFloat()
var row5 = CGFloat()
var targetColor = UIColor()
var isPermanent = Bool()
var hasThreeColors = Bool()
var secondColor = UIColor()
var position1 = CGPoint()
var startColor = UIColor()
var square = SKSpriteNode()
override func didMove(to view: SKView) {
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if started == false{
started = true
start()
for i in 0 ... 24{
targetColor = targetColors[i]
isPermanent = permanent[i]
hasThreeColors = has3Colors[i]
secondColor = secondColors[i]
position1 = positions[i]
startColor = startColors[i]
square = squares[i]
createSquare(targetColor: targetColor, permanent: isPermanent, has3Colors: hasThreeColors, secondColor: secondColor, position: position1, startColor: startColor, newSquare: square)
}
self.addChild(square1)
self.addChild(square2)
self.addChild(square3)
self.addChild(square4)
self.addChild(square5)
self.addChild(square6)
self.addChild(square7)
self.addChild(square8)
self.addChild(square9)
self.addChild(square10)
self.addChild(square11)
self.addChild(square12)
self.addChild(square13)
self.addChild(square14)
self.addChild(square15)
self.addChild(square16)
self.addChild(square17)
self.addChild(square18)
self.addChild(square19)
self.addChild(square20)
self.addChild(square21)
self.addChild(square22)
self.addChild(square23)
self.addChild(square24)
self.addChild(square25)
}
circle.run(SKAction.moveTo(x: location.x, duration: 0.2))
}
}
func createSquare(targetColor: UIColor, permanent: Bool, has3Colors: Bool, secondColor: UIColor, position: CGPoint, startColor:UIColor, newSquare: SKSpriteNode){
var border = SKSpriteNode()
var firstTouch = Bool()
border = SKSpriteNode(imageNamed: "BorderTile")
newSquare.size = basicSize
newSquare.position = position
newSquare.color = startColor
border.size = newSquare.size
border.position = newSquare.position
firstTouch = true
self.addChild(border)
if permanent{
border.color = targetColor
}else{
let targetColor1 = targetColor.darker()
border.color = targetColor1
}
func start(){
basicSize = CGSize(width: self.frame.width / 7, height: self.frame.width / 7)
createCircle()
getArrays()
}
func getArrays(){
rowsAndColumbs()
squares = [square1,square2,square3,square4,square5,square6,square7,square8,square8,square10,square11,square12,square13,square14,square15,square16,square17,square1,square19,square20,square21,square22,square23,square24,square25]
positions = [CGPoint(x: row1,y:columb1),CGPoint(x: row1,y:columb2),CGPoint(x: row1,y:columb3),CGPoint(x: row1,y:columb4),CGPoint(x: row1,y:columb5),CGPoint(x: row2,y:columb1),CGPoint(x: row2,y:columb2),CGPoint(x: row2,y:columb3),CGPoint(x: row2,y:columb4),CGPoint(x: row2,y:columb5),CGPoint(x: row3,y:columb1),CGPoint(x: row3,y:columb2),CGPoint(x: row3,y:columb3),CGPoint(x: row3,y:columb4),CGPoint(x: row3,y:columb5),CGPoint(x: row4,y:columb1),CGPoint(x: row4,y:columb2),CGPoint(x: row4,y:columb1),CGPoint(x: row4,y:columb1),CGPoint(x: row4,y:columb1),CGPoint(x: row5,y:columb1),CGPoint(x: row5,y:columb2),CGPoint(x: row5,y:columb3),CGPoint(x: row5,y:columb4),CGPoint(x: row5,y:columb5)]
loadLvl1()
}
func loadLvl1(){
var targetColors1 = Array<UIColor>()
var permanent1 = Array<Bool>()
var has3Colors1 = Array<Bool>()
var secondColors1 = Array<UIColor>()
var startColors1 = Array<UIColor>()
targetColors1 = [.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green,.green]
permanent1 = [true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true,true]
secondColors1 = [.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear,.clear]
has3Colors1 = [false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false,false]
startColors1 = [.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue,.blue]
var lvl1Array = Array<Array<Any>>()
lvl1Array = [targetColors1,permanent1,has3Colors1,secondColors1,startColors1]
targetColors = lvl1Array[0] as! Array<UIColor>
permanent = lvl1Array[1] as! Array<Bool>
has3Colors = lvl1Array[2] as! Array<Bool>
secondColors = lvl1Array[3] as! Array<UIColor>
startColors = lvl1Array[4] as! Array<UIColor>
}
func rowsAndColumbs(){
columb1 = CGFloat( basicSize.width * 1.5)
columb2 = CGFloat( basicSize.width * 2.5)
columb3 = CGFloat( basicSize.width * 3.5)
columb4 = CGFloat( basicSize.width * 4.5)
columb5 = CGFloat( basicSize.width * 5.5)
row1 = CGFloat( basicSize.height * 5.5)
row1 = CGFloat( basicSize.height * 4.5)
row1 = CGFloat( basicSize.height * 3.5)
row1 = CGFloat( basicSize.height * 2.5)
row1 = CGFloat( basicSize.height * 1.5)
}
func createCircle(){
circle = SKSpriteNode(imageNamed: "Circle")
circle.size = CGSize(width: self.frame.width / 7, height: self.frame.width / 7)
circle .position = CGPoint(x: 50, y: 50)
self.addChild(circle)
//alive = true
}
override func update(_ currentTime: TimeInterval) {}
// Called before each frame is rendered
}
What i have done is there is a function that uses an array of arrays of values to make the grid but i know there would be a better way to do this and shorten it but i don't know how. I have considered structs but still they would be really long sense i have so any values and i know there is a better way to do this so can someone tell me.
Well, I'll just give you one hint. Think about replacing this:
var columb1 = CGFloat()
var columb2 = CGFloat()
var columb3 = CGFloat()
var columb4 = CGFloat()
var columb5 = CGFloat()
// ...
columb1 = CGFloat( basicSize.width * 1.5)
columb2 = CGFloat( basicSize.width * 2.5)
columb3 = CGFloat( basicSize.width * 3.5)
columb4 = CGFloat( basicSize.width * 4.5)
columb5 = CGFloat( basicSize.width * 5.5)
with this:
var columns : [CGFloat] = (1...5).map {basicSize.width * (CGFloat($0) + 0.5)}
Do you see the difference? I don't just mean the fact that we've reduced 10 lines to 1 line. I mean that we completely eliminated columb1, columb2, and so on — instead, we are using one variable, an array, and we're using it as an array.
You have a feeling something is wrong, and you're quite right. There is no need for all your intermediate variables with numbers on the end of the name. Throw them all away. An array serves exactly that purpose; that is what an array is — a name together with a number (the index number) that references one of many values. Use patterns, loops, and arrays for storage; that is what basic programming is.
You have a number of arrays that contain 25 items, one for each square. What you really need is a class or struct that represents a square that has the properties held by those arrays:
class Square {
var node = SKSpriteNode(imageNamed: "SquareTile")
var startColor = UIColor.blue
var secondColor = UIColor.clear
var targetColor = UIColor.green
var permanent = true
var has3Colors = false
}
// create the array of squares
var squares = (1...25).map { _ in Square() }
// add the nodes as children of self
squares.forEach { self.addChild($0.node) }
You then access each square by index: square[0], square[1], all the way to square[24].
The properties are accessed like square[0].startColor and square[7].has3Colors.

Trouble converting custom CGRect function from Objective-C to Swift

I've been going back and forth on trying to convert a custom CGRect function from Objective-C to Swift.
I'll make some small progress but then I always end up getting stuck. Here is the working function in Objective-C:
CGRect CGRectSmallestWithCGPoints(NSMutableArray *pointsArray, int numberOfPoints) {
NSValue *firstValue = pointsArray[0];
CGFloat greatestXValue = [firstValue CGPointValue].x;
CGFloat greatestYValue = [firstValue CGPointValue].y;
CGFloat smallestXValue = [firstValue CGPointValue].x;
CGFloat smallestYValue = [firstValue CGPointValue].y;
for(int i = 1; i < numberOfPoints; i++) {
NSValue *value = pointsArray[i];
CGPoint point = [value CGPointValue];
greatestXValue = MAX(greatestXValue, point.x);
greatestYValue = MAX(greatestYValue, point.y);
smallestXValue = MIN(smallestXValue, point.x);
smallestYValue = MIN(smallestYValue, point.y);
}
CGRect rect;
rect.origin = CGPointMake(smallestXValue, smallestYValue);
rect.size.width = greatestXValue - smallestXValue;
rect.size.height = greatestYValue - smallestYValue;
return rect;
}
Here's where I currently am with the Swift conversion:
func CGRectSmallestWithCGPoints(pointsArray: NSArray, numberOfPoints: Int) -> CGRect {
var greatestXValue = pointsArray[0].x
var greatestYValue = pointsArray[0].y
var smallestXValue = pointsArray[0].x
var smallestYValue = pointsArray[0].y
for(var i = 1; i < numberOfPoints; i++)
{
let point = pointsArray[i];
greatestXValue = max(greatestXValue, point.x);
greatestYValue = max(greatestYValue, point.y);
smallestXValue = min(smallestXValue, point.x);
smallestYValue = min(smallestYValue, point.y);
}
var rect = CGRect()
rect.origin = CGPointMake(smallestXValue, smallestYValue);
rect.size.width = greatestXValue - smallestXValue;
rect.size.height = greatestYValue - smallestYValue;
return rect;
}
The errors start in the for loop. When I try to use max and min it gives me the following error:
Cannot assign a value of type 'CLHeadingComponentValue' (aka 'Double') to a value of type 'CLHeadingComponentValue!'
And then after the for loop when I modify the rect values it gives me a similar error:
Cannot assign a value of type 'CLHeadingComponentValue' (aka 'Double') to a value of type 'CGFloat'
I'm having trouble understanding why this conversion seems so hard. I've been using Swift on and off for the past few weeks now and I've gotten stuck on certain things like some of the optional concepts but never been stuck on something for this long before.
I'm using Xcode 7 beta with Swift 2 and would really appreciate your help thanks.
You have some mistakes in your code, you have to use the CGPointValue property instead of the x property directly see the fixed code:
func CGRectSmallestWithCGPoints(pointsArray: NSArray, numberOfPoints: Int) -> CGRect {
var greatestXValue: CGFloat = pointsArray[0].CGPointValue.x
var greatestYValue: CGFloat = pointsArray[0].CGPointValue.y
var smallestXValue: CGFloat = pointsArray[0].CGPointValue.x
var smallestYValue: CGFloat = pointsArray[0].CGPointValue.y
for(var i = 1; i < numberOfPoints; i++) {
let point = pointsArray[i].CGPointValue
greatestXValue = max(greatestXValue, point.x)
greatestYValue = max(greatestYValue, point.y)
smallestXValue = min(smallestXValue, point.x)
smallestYValue = min(smallestYValue, point.y)
}
var rect = CGRect()
rect.origin = CGPointMake(smallestXValue, smallestYValue);
rect.size.width = greatestXValue - smallestXValue;
rect.size.height = greatestYValue - smallestYValue;
return rect
}
I hope this help you.
A more functional approach:
func CGRectSmallestWithCGPoints(pointsArray: [CGPoint]) -> CGRect? {
if pointsArray.isEmpty {
return nil
}
let minX = pointsArray.reduce(CGFloat.max) { min, point in min(min, point.x) }
let minY = pointsArray.reduce(CGFloat.max) { min, point in min(min, point.y) }
let maxX = pointsArray.reduce(CGFloat.min) { max, point in max(max, point.x) }
let maxY = pointsArray.reduce(CGFloat.min) { max, point in max(max, point.y) }
let rect = CGRect(x: minX, y: minY, width: maxX - minX, height: maxY - minY)
return rect
}

How to fix this NSTimer issue?

Everything is fine until I use NSTimer to run the tree, that the images of tree is gone. I use SKview to show the physic then I find out that the code is still run, just only the image is gone. How can I fix it?
Part 1
class PlayScene: SKScene {
var BG6 = SKSpriteNode()
var balloon = SKSpriteNode()
var tree1 = SKSpriteNode()
var tree2 = SKSpriteNode()
Part 2
override func didMoveToView(view: SKView) {
MakeBG()
// add balloon
var ballontexture = SKTexture(imageNamed: "Balloon.png")
balloon = SKSpriteNode(texture: ballontexture)
balloon.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/5)
balloon.physicsBody = SKPhysicsBody(rectangleOfSize: balloon.size)
balloon.physicsBody?.dynamic = true
balloon.physicsBody?.allowsRotation = false
balloon.zPosition = 5
self.addChild(balloon)
var timer = NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("MakeTree"), userInfo: nil, repeats: true)
var ground = SKNode()
ground.position = CGPointMake(0, 0)
ground.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(self.frame.size.width,1))
ground.physicsBody?.dynamic = false
self.addChild(ground)
}
Part 3
func MakeBG(){
// chen hinh nen
//BG1
var BG1texture = SKTexture(imageNamed: "BG2")
var BG1 = SKSpriteNode(texture: BG1texture)
BG1.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2)
BG1.size.height = self.size.height
var BG1move = SKAction.moveToY(-self.frame.size.height, duration: 90)
BG1.runAction(BG1move)
self.addChild(BG1)
//BG2
var BG2texture = SKTexture(imageNamed: "BG3")
var BG2 = SKSpriteNode(texture: BG2texture)
BG2.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2 + self.frame.size.height)
BG2.size.height = self.size.height
var BG2move = SKAction.moveToY(-self.frame.size.height, duration: 140)
BG2.runAction(BG2move)
self.addChild(BG2)
//BG3
var BG3texture = SKTexture(imageNamed: "BG4")
var BG3 = SKSpriteNode(texture: BG3texture)
BG3.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2 + self.frame.size.height*2)
BG3.size.height = self.size.height
var BG3move = SKAction.moveToY(-self.frame.size.height, duration: 195)
BG3.runAction(BG3move)
self.addChild(BG3)
//BG4
var BG4texture = SKTexture(imageNamed: "BG5")
var BG4 = SKSpriteNode(texture: BG4texture)
BG4.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2 + self.frame.size.height*3)
BG4.size.height = self.size.height
var BG4move = SKAction.moveToY(-self.frame.size.height, duration: 250)
BG4.runAction(BG4move)
self.addChild(BG4)
//BG5
var BG5texture = SKTexture(imageNamed: "BG6")
var BG5 = SKSpriteNode(texture: BG5texture)
BG5.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2 + self.frame.size.height*4)
BG5.size.height = self.size.height
var BG5move = SKAction.moveToY(-self.frame.size.height, duration: 305)
BG5.runAction(BG5move)
self.addChild(BG5)
// BG6
var BG6texture = SKTexture(imageNamed: "BG7")
BG6 = SKSpriteNode(texture: BG6texture)
BG6.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height/2 + self.frame.size.height*5 + self.frame.size.height*5)
BG6.size.height = self.size.height
BG6.runAction(SKAction.moveTo(CGPoint(x: self.size.width/2 , y: self.size.height/2), duration: 276.5))
self.addChild(BG6)
}
func MakeTree(){
// between
let between = balloon.size.width
// random
var movementAmount = arc4random() % UInt32(self.frame.size.height/2)
var treeoffset = CGFloat(movementAmount) - self.frame.size.height/4
// move tree
var treemove = SKAction.moveByX(0, y: -self.frame.size.width*2, duration: NSTimeInterval(self.frame.size.height/50))
var treeremove = SKAction.removeFromParent()
var treemoveandremove = SKAction.sequence([treemove, treeremove])
// add tree1
var tree1texture = SKTexture(imageNamed: "C2.png")
tree1 = SKSpriteNode(texture: tree1texture)
tree1.position = CGPointMake(CGRectGetMidX(self.frame) - tree1texture.size().width/2 - between + treeoffset, CGRectGetMidY(self.frame) + self.frame.size.height)
tree1.runAction(treemoveandremove)
tree1.physicsBody = SKPhysicsBody(rectangleOfSize: tree1.size)
tree1.physicsBody?.dynamic = false
self.addChild(tree1)
// add tree2
var tree2texture = SKTexture(imageNamed: "C1.png")
tree2 = SKSpriteNode(texture: tree2texture)
tree2.position = CGPointMake(CGRectGetMidX(self.frame) + tree2texture.size().width/2 + between + treeoffset, CGRectGetMidY(self.frame) + self.frame.size.height)
tree2.runAction(treemoveandremove)
tree2.physicsBody = SKPhysicsBody(rectangleOfSize: tree2.size)
tree2.physicsBody?.dynamic = false
[enter image description here][1]
self.addChild(tree2)
}
If I understood your question correctly, then you want to know why your "tree" is gone.
It appears that your MakeTree() method moves all the tree sprite nodes off the screen:
var treemove = SKAction.moveByX(0, y: -self.frame.size.width*2, duration: NSTimeInterval(self.frame.size.height/50))
Also, you shouldn't use NSTimers in SpriteKit. Use SKAction.waitForDuration instead. Like this:
let delay = SKAction.waitForDuration(3)
someNode.runAction(delay) {
//run code here after 3 seconds
}

Error using waitforduration function

import SpriteKit
class GameScene: SKScene {
var greenBall = SKSpriteNode(imageNamed: "greenball")
var redBall = SKSpriteNode(imageNamed: "redball")
override func didMoveToView(view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
backgroundColor = SKColor.purpleColor()
//
var bar0 = SKSpriteNode(imageNamed: "bar0")
var bar1 = SKSpriteNode(imageNamed: "bar1")
var bar2 = SKSpriteNode(imageNamed: "bar2")
var bar3 = SKSpriteNode(imageNamed: "bar3")
var bar4 = SKSpriteNode(imageNamed: "bar4")
var bar5 = SKSpriteNode(imageNamed: "bar5")
var bars = SKSpriteNode(imageNamed: "bar\(arc4random_uniform(6))")
var bartest = SKSpriteNode(imageNamed: "bar0")
bartest.position = CGPoint(x: frame.midX, y: frame.size.height * 0.633)
addChild(bartest)
var barra = SKShapeNode(rectOfSize: CGSize(width: frame.size.width, height: 10))
barra.name = "bar"
barra.fillColor = SKColor.whiteColor()
barra.position = CGPoint(x: frame.midX, y: frame.midY)
greenBall.position = CGPoint(x: frame.midX-100, y: frame.midY)
greenBall.yScale = 2
greenBall.xScale = 2
redBall.position = CGPoint(x: frame.midX+100, y: frame.midY)
redBall.yScale = 2
redBall.xScale = 2
self.addChild(redBall)
self.addChild(greenBall)
self.addChild(barra)
func makeBars() {
var randomTopOrBot = arc4random_uniform(2)
if randomTopOrBot == 0 {
var MaxY = frame.size.height * 0.951
var MinY = (frame.size.height * 0.633)-bars.size.height
var Range = CGFloat(MaxY - MinY)
var barsSpawnX = (frame.midX)
var barSpawnY = CGFloat(arc4random_uniform(UInt32(Range)))
bars.position = CGPoint(x: frame.midX, y: barSpawnY)
addChild(bars)
} else {
var MaxY = frame.size.height * 0.951
var MinY = (frame.size.height * 0.633)-bars.size.height
var Range = CGFloat(MaxY - MinY)
var barsSpawnX = (frame.midX)
var randomBetweenRange = CGFloat(arc4random_uniform(UInt32((Range))))
bars.position = CGPoint(x: frame.midX, y: (MinY+randomBetweenRange))
println(frame.size.height * 0.633)
addChild(bars)
}
}
let wait = SKAction.waitForDuration(3, withRange: 2)
let spawn = SKAction.runBlock { makeBars <--- this is where it's saying the problem is()
}
let sequence = SKAction.sequence([wait, spawn])
self.runAction(SKAction.repeatActionForever(sequence))
edit: So basically I've got 2 spawn locations I'm picking from, I randomly decide between the two with a random number, and now I'm wanting to apply the timer to the function makeBars, but it's telling me I can't reference the local function
This is giving me an error that says "cannot reference a local function with captures from another local function".
How can I fix this?
You declared your makerbars() method inside the didMoveToView method. So it was a function nesting problem after all, given the full code I was able to see the true issue.
Change your code to my modified and corrected version below:
import SpriteKit
class GameScene: SKScene {
var greenBall = SKSpriteNode(imageNamed: "greenball")
var redBall = SKSpriteNode(imageNamed: "redball")
override func didMoveToView(view: SKView) {
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
backgroundColor = SKColor.purpleColor()
//
var bar0 = SKSpriteNode(imageNamed: "bar0")
var bar1 = SKSpriteNode(imageNamed: "bar1")
var bar2 = SKSpriteNode(imageNamed: "bar2")
var bar3 = SKSpriteNode(imageNamed: "bar3")
var bar4 = SKSpriteNode(imageNamed: "bar4")
var bar5 = SKSpriteNode(imageNamed: "bar5")
var bartest = SKSpriteNode(imageNamed: "bar0")
bartest.position = CGPoint(x: frame.midX, y: frame.size.height * 0.633)
addChild(bartest)
var barra = SKShapeNode(rectOfSize: CGSize(width: frame.size.width, height: 10))
barra.name = "bar"
barra.fillColor = SKColor.whiteColor()
barra.position = CGPoint(x: frame.midX, y: frame.midY)
greenBall.position = CGPoint(x: frame.midX-100, y: frame.midY)
greenBall.yScale = 2
greenBall.xScale = 2
redBall.position = CGPoint(x: frame.midX+100, y: frame.midY)
redBall.yScale = 2
redBall.xScale = 2
self.addChild(redBall)
self.addChild(greenBall)
self.addChild(barra)
let wait = SKAction.waitForDuration(3, withRange: 2)
let spawn = SKAction.runBlock { self.makeBars() }
let sequence = SKAction.sequence([wait, spawn])
self.runAction(SKAction.repeatActionForever(sequence))
}
func makeBars() {
var bars = SKSpriteNode(imageNamed: "bar\(arc4random_uniform(6))")
var randomTopOrBot = arc4random_uniform(2)
if randomTopOrBot == 0 {
var MaxY = frame.size.height * 0.951
var MinY = (frame.size.height * 0.633)-bars.size.height
var Range = CGFloat(MaxY - MinY)
var barsSpawnX = (frame.midX)
var barSpawnY = CGFloat(arc4random_uniform(UInt32(Range)))
bars.position = CGPoint(x: frame.midX, y: barSpawnY)
addChild(bars)
} else {
var MaxY = frame.size.height * 0.951
var MinY = (frame.size.height * 0.633)-bars.size.height
var Range = CGFloat(MaxY - MinY)
var barsSpawnX = (frame.midX)
var randomBetweenRange = CGFloat(arc4random_uniform(UInt32((Range))))
bars.position = CGPoint(x: frame.midX, y: (MinY+randomBetweenRange))
println(frame.size.height * 0.633)
addChild(bars)
}
}
}

SKAction not working as expected

Hi I have a sequence of action that run forever to generate obstacles in a side scrolling game. But instead of generating random pictures and heights from my textures it keeps the same ones it generated the first time it ran the SKActions...
Can someone solve this? I am trying not to use
NSTimer.scheduledTimerWithTimeInterval(4, target: self, selector: Selector("setUpMountains"), userInfo: nil, repeats: true)
As it lowers my FPS for some reason
func setUpMountains() {
let gapHeight = user.size.height * 3
var movementAmount = arc4random() % UInt32(self.frame.size.height / 3.0)
var randomNumber = arc4random() % 3
if randomNumber == 0 {
mountainTexture = SKTexture(imageNamed:"RedMountain.png")
} else if randomNumber == 1 {
mountainTexture = SKTexture(imageNamed:"OrangeMountain.png")
} else {
mountainTexture = SKTexture(imageNamed:"BeigeMountain.png")
}
var randomMountain = SKSpriteNode(texture: mountainTexture)
randomMountain.position = CGPointMake( CGRectGetMidX(self.frame) + self.frame.width, CGRectGetMidY(self.frame) - gapHeight - CGFloat(movementAmount))
randomMountain.zPosition = 8
randomMountain.physicsBody = SKPhysicsBody(texture: mountainTexture, size: mountainTexture.size())
randomMountain.physicsBody?.dynamic = false
// randomMountain.physicsBody?.categoryBitMask = objectCategory
movingObjects.addChild(randomMountain)
//spawning mountains
var distanceToMove = self.frame.size.width + mountainTexture.size().width
var moveMountain = SKAction.moveByX(-distanceToMove, y: 0, duration: NSTimeInterval (distanceToMove * 0.01))
var replaceMountain = SKAction.moveByX(distanceToMove, y: 0, duration: 0)
//var removeMountain = SKAction.removeFromParent()
var moveAndRemoveMountain = SKAction.repeatActionForever(SKAction.sequence([moveMountain, replaceMountain]))
randomMountain.runAction(moveAndRemoveMountain)
}
Instead of loading the textures each time, cache the textures once in an array, for e.g. mountains in the following code.
Use an SKAction sequence with a wait duration instead of an NSTimer to repeatedly execute functions.
You don't need the repeatActionForever action in the setup mountains since the generated obstacles are removed once they go out of the screen using removeParent.
var mountains : [SKTexture] = []
override func didMoveToView(view: SKView) {
mountains = [SKTexture(imageNamed:"RedMountain.png"),SKTexture(imageNamed:"OrangeMountain.png"),SKTexture(imageNamed:"BeigeMountain.png")]
let generateMountains = SKAction.runBlock { () -> Void in
self.setUpMountains()
}
let generateObstaclesPeriodically = SKAction.repeatActionForever (SKAction.sequence([generateMountains,SKAction.waitForDuration(4.0)]))
self.runAction(generateObstaclesPeriodically)
}
func setUpMountains() {
let gapHeight = user.size.height * 3
var movementAmount = arc4random() % UInt32(self.frame.size.height / 3.0)
var randomNumber = arc4random() % 3
let mountainTexture = mountains[randomNumber]
var randomMountain = SKSpriteNode(texture: mountainTexture)
randomMountain.position = CGPointMake( CGRectGetMidX(self.frame) + self.frame.width, CGRectGetMidY(self.frame) - gapHeight - CGFloat(movementAmount))
randomMountain.zPosition = 8
randomMountain.physicsBody = SKPhysicsBody(texture: mountainTexture, size: mountainTexture.size())
randomMountain.physicsBody?.dynamic = false
movingObjects.addChild(randomMountain)
//spawning mountains
var distanceToMove = self.frame.size.width + mountainTexture.size().width
var moveMountain = SKAction.moveByX(-distanceToMove, y: 0, duration: NSTimeInterval (distanceToMove * 0.01))
var removeMountain = SKAction.removeFromParent()
var moveAndRemoveMountain = SKAction.sequence([moveMountain, removeMountain])
randomMountain.runAction(moveAndRemoveMountain)
}
To remove an SKAction you can give a key to the SKAction while adding it and use the key to remove it from the node.
self.runAction(generateObstaclesPeriodically, withKey: "generateObstaclesPeriodically")
self.removeActionForKey("generateObstaclesPeriodically")
#rakeshbs thanks a lot that seems to work but i changed it to this instead
mountains = [SKTexture(imageNamed:"RedMountain.png"),SKTexture(imageNamed:"OrangeMountain.png"),SKTexture(imageNamed:"BeigeMountain.png")]
let generateMountains = SKAction.runBlock { () -> Void in
self.setUpMountains()
}
let generateObstaclesPeriodically = SKAction.repeatActionForever(SKAction.sequence([generateMountains,SKAction.waitForDuration(4.0)]))
self.runAction(generateObstaclesPeriodically)