SWIFT: How to randomly select 1 of 5 obstacles and run an action on it, and repeat this process every second? - swift

I am trying to create a game where there are five different obstacles, one of which is selected at random every second, and moves from the top of the screen to the bottom of the screen. This should create an obstacle field for the player to navigate. I am able to have the first obstacle move down the screen, but instead of another coming down the screen a second later, I keep getting Thread 1: signal SIGABRT errors, despite trying to fix the problem.
Here is my code:
func randomize() {
smallMiddleObstacle.size = CGSizeMake(self.frame.width - 180, obstacleHeight)
smallMiddleObstacle.position = CGPointMake(self.frame.width / 2, self.frame.height + smallMiddleObstacle.frame.height / 4)
smallMiddleObstacle.color = UIColor.blueColor()
bigMiddleObstacle.size = CGSizeMake(self.frame.width - 100, obstacleHeight)
bigMiddleObstacle.position = CGPointMake(self.frame.width / 2, self.frame.height + bigMiddleObstacle.frame.height / 4)
bigMiddleObstacle.color = UIColor.blueColor()
rightObstacle.size = CGSizeMake(self.frame.width * 1.4, obstacleHeight)
rightObstacle.position = CGPointMake(self.frame.width, self.frame.height + rightObstacle.frame.height / 4)
rightObstacle.color = UIColor.blueColor()
leftObstacle.size = CGSizeMake(self.frame.width * 1.4, obstacleHeight)
leftObstacle.position = CGPointMake(0, self.frame.height + leftObstacle.frame.height / 4)
leftObstacle.color = UIColor.blueColor()
rightObstacleInPair.size = CGSizeMake(self.frame.width * 0.7, obstacleHeight)
rightObstacleInPair.position.x = self.frame.width
rightObstacleInPair.color = UIColor.blueColor()
obstaclePair.addChild(rightObstacleInPair)
leftObstacleInPair.size = CGSizeMake(self.frame.width * 0.7, obstacleHeight)
leftObstacleInPair.position.x = 0
leftObstacleInPair.color = UIColor.blueColor()
obstaclePair.addChild(leftObstacleInPair)
obstaclePair.position.y = self.frame.height + obstaclePair.frame.height / 4
let distance = CGFloat(self.frame.height + obstacleHeight)
let move = SKAction.moveByX(0, y: -distance, duration: NSTimeInterval(0.005 * distance))
let remove = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([move, remove])
let random = Int(arc4random_uniform(4))
if random == 0 {
addChild(smallMiddleObstacle)
smallMiddleObstacle.runAction(moveAndRemove)
} else if random == 1 {
addChild(bigMiddleObstacle)
bigMiddleObstacle.runAction(moveAndRemove)
} else if random == 2 {
addChild(rightObstacle)
rightObstacle.runAction(moveAndRemove)
} else if random == 3 {
addChild(leftObstacle)
leftObstacle.runAction(moveAndRemove)
} else {
addChild(obstaclePair)
obstaclePair.runAction(moveAndRemove)
}
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if !isGameStarted {
isGameStarted = true
let spawn = SKAction.runBlock({
() in
self.randomize()
})
let delay = SKAction.waitForDuration(1.5)
let spawnDelay = SKAction.sequence([spawn, delay])
let spawnDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spawnDelayForever)
}
}
Thanks

You forgot to removeFromParent the already addChild's so, the second time you call randomizemethod your have an "Attemped to add a SKNode which already has a parent:"
First of all give a name to your nodes if you have not already done.
Everytime before you make an addChild, you must be sure you don't already have added it, to know you can do:
For example:
if let child = self.childNodeWithName(smallMiddleObstacle.name) {
child.removeFromParent()
}

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?

How to identify specific nodes in SKCamera's view?

I am trying to recycle same SKSpriteNode to make continuous background. I created SKSpriteNode as bg0, bg1, bg2 and positioned them properly when presenting the scene. Somehow, below code ONLY re-positions bg0 once.
cam is SKCameraNode, therefore, I am checking whether camera contains the background nodes. Due to their size, always one of them should not be visible in the camera viewport. However, like I said this only works once and when camera shows recycled bg0 does not recognize it.
Thank you in advance.
PS: I already tried cam.intersects(bg0) as well, got the same result.
func updateBgPos() {
if (player?.position.y)! > self.frame.height {
if !cam.contains(bg0!) {
print("bg0 is not in the scene")
newPosBgY = (bg2?.position.y)! + self.frame.height
bg0?.physicsBody = nil
bg0?.position = CGPoint(x: self.frame.width / 2, y: newPosBgY)
bg0?.physicsBody = bg1?.physicsBody
} else if !cam.contains(bg1!) {
print("bg1 is not in the scene")
newPosBgY = (bg0?.position.y)! + self.frame.height
bg1?.physicsBody = nil
bg1?.position = CGPoint(x: self.frame.width / 2, y: newPosBgY)
bg1?.physicsBody = bg0?.physicsBody
} else if !cam.contains(bg2!) {
print("bg2 is not in the scene")
newPosBgY = (bg1?.position.y)! + self.frame.height
bg2?.physicsBody = nil
bg2?.position = CGPoint(x: self.frame.width / 2, y: newPosBgY)
bg2?.physicsBody = bg1?.physicsBody
}
}
}
Well, finally I've figured it out how to achieve this. I hope this helps someone else.
I initially created and placed two SKSpriteNode as background as bg0 and bg1 like below:
bg0 = SKSpriteNode(imageNamed: "bg0")
bg1 = SKSpriteNode(imageNamed: "bg1")
bg0!.name = "bg0"
bg1!.name = "bg1"
bg0?.scale(to: CGSize(width: sceneWidth, height: sceneHeight))
bg1?.scale(to: CGSize(width: sceneWidth, height: sceneHeight))
bg0?.zPosition = zPosBg
bg1?.zPosition = zPosBg
backgroundArr.append(bg0!)
backgroundArr.append(bg1!)
bg0?.position = CGPoint(x: sceneWidth / 2, y: 0)
bg1?.position = CGPoint(x: sceneWidth / 2, y: sceneHeight)
self.addChild(bg0!)
self.addChild(bg1!)
After, on the update method I called below function:
func updateBgPos() {
guard let playerPosY = player?.position.y else { return }
guard let bg0PosY = bg0?.position.y else { return }
guard let bg1PosY = bg1?.position.y else { return }
if playerPosY - bg0PosY > sceneHeight / 2 + camFollowGap {
print("bg0 is not in the scene")
newPosBgY = bg1PosY + sceneHeight
bg0?.position = CGPoint(x: sceneWidth / 2, y: newPosBgY)
} else if playerPosY - bg1PosY > sceneHeight / 2 + camFollowGap {
print("bg1 is not in the scene")
newPosBgY = bg0PosY + sceneHeight
bg1?.position = CGPoint(x: sceneWidth / 2, y: newPosBgY)
}
}
PS: camFollowGap is CGFloat value that I subtracted when I position my camera to the player. When dealing with continuous background I had to add that value into calculation to avoid positioning delay and appearances of temporary gaps between backgrounds.

Spritekit - Shifting between two functions with a timer

I'm making a game where I have two different functions that holds different types of enemies. I want to make a code that shifts between the two every 10 score. So from 0-10 "function 1" is active, and from 10-20 "function 2" is active, and then it changes back again and then back again and so on.
This is my two functions containing enemies:
var score = 0
func createPipes() {
PipesHolder = SKNode()
PipesHolder.name = "Pipe"
let pipeLeft = SKSpriteNode(imageNamed: "PipeRight")
pipeLeft.name = "Pipe"
pipeLeft.anchorPoint = CGPoint(x: 0.5, y: 0.5)
pipeLeft.position = CGPoint(x: 300, y: 0)
pipeLeft.physicsBody = SKPhysicsBody(rectangleOf: pipeLeft.size)
pipeLeft.physicsBody?.categoryBitMask = ColliderType.Pipe
pipeLeft.physicsBody?.affectedByGravity = false
let pipeRight = SKSpriteNode(imageNamed: "PipeLeft")
pipeRight.name = "Pipe"
pipeRight.anchorPoint = CGPoint(x: 0.5, y: 0.5)
pipeRight.position = CGPoint(x: -300, y: 0)
pipeRight.physicsBody = SKPhysicsBody(rectangleOf: pipeRight.size)
pipeRight.physicsBody?.categoryBitMask = ColliderType.Pipe
pipeRight.physicsBody?.affectedByGravity = false
PipesHolder.zPosition = 2
PipesHolder.xScale = 1.5
PipesHolder.yScale = 0.8
PipesHolder.position.x = CGFloat.randomBetweenNumbers(firstNum:
-220, secondNum: 220)
PipesHolder.position.y = self.frame.height + 100
PipesHolder.addChild(pipeLeft)
PipesHolder.addChild(pipeRight)
self.addChild(PipesHolder)
let destination = self.frame.height * 2
let move = SKAction.moveTo(y: -destination, duration: 10)
let remove = SKAction.removeFromParent()
let moveRight = SKAction.moveBy(x: 200, y: 0, duration: 1)
let moveLeft = SKAction.moveBy(x: -200, y: 0, duration: 1)
let moveBackAndForth =
SKAction.repeatForever(SKAction.sequence([moveRight, moveLeft]))
PipesHolder.run(moveBackAndForth)
PipesHolder.run(SKAction.sequence([move, remove]), withKey:
"MovePipes")
}
func spawnPipes() {
let spawn = SKAction.run({ () -> Void in
self.createPipes()
})
let delay = SKAction.wait(forDuration: 1)
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "SpawnPipes")
}
func createRedEnemies() {
let enemyHolder = SKNode()
enemyHolder.name = "Holder"
let enemyLeft = SKSpriteNode(imageNamed: "Enemy")
let enemyMiddle = SKSpriteNode(imageNamed: "Enemy")
let enemyRight = SKSpriteNode(imageNamed: "Enemy")
enemyLeft.name = "Enemy"
enemyLeft.anchorPoint = CGPoint(x: 0.5, y: 0.5)
enemyLeft.position = CGPoint(x: 200, y: 0)
enemyLeft.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:
enemyLeft.size.width - 5, height: enemyLeft.size.height - 5))
enemyLeft.physicsBody?.categoryBitMask = ColliderType.Enemy
enemyLeft.physicsBody?.collisionBitMask = 0
enemyLeft.physicsBody?.affectedByGravity = false
enemyMiddle.name = "Enemy"
enemyMiddle.anchorPoint = CGPoint(x: 0.5, y: 0.5)
enemyMiddle.position = CGPoint(x: 0, y: 0)
enemyMiddle.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:
enemyMiddle.size.width - 5, height: enemyMiddle.size.height - 5))
enemyMiddle.physicsBody?.categoryBitMask = ColliderType.Enemy
enemyLeft.physicsBody?.collisionBitMask = 0
enemyMiddle.physicsBody?.affectedByGravity = false
enemyRight.name = "Enemy"
enemyRight.anchorPoint = CGPoint(x: 0.5, y: 0.5)
enemyRight.position = CGPoint(x: -200, y: 0)
enemyRight.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:
enemyRight.size.width - 5, height: enemyRight.size.height - 5))
enemyRight.physicsBody?.categoryBitMask = ColliderType.Enemy
enemyLeft.physicsBody?.collisionBitMask = 0
enemyRight.physicsBody?.affectedByGravity = false
enemyHolder.zPosition = 2
enemyHolder.position.y = self.frame.height + 100
enemyHolder.position.x = CGFloat.randomBetweenNumbers(firstNum:
-100, secondNum: 100)
enemyHolder.addChild(enemyLeft)
enemyHolder.addChild(enemyMiddle)
enemyHolder.addChild(enemyRight)
self.addChild(enemyHolder)
let destination = self.frame.height * 4
let move = SKAction.moveTo(y: -destination, duration: 9)
let remove = SKAction.removeFromParent()
enemyHolder.run(SKAction.sequence([move, remove]), withKey:
"MoveEnemies")
}
func spawnEnemies() {
let spawn = SKAction.run({ () -> Void in
self.createRedEnemies()
})
let delay = SKAction.wait(forDuration: 0.4)
let sequence = SKAction.sequence([spawn, delay])
self.run(SKAction.repeatForever(sequence), withKey: "SpawnEnemies")
}
Here is the code I have for the shifted functions that I add in the "didMove":
func shiftEnemies() {
if score >= 0 && score <= 10 {
spawnEnemies()
} else if score >= 11 && score <= 20 {
spawnPipes()
} else if score >= 21 && score <= 30 {
spawnEnemies()
} else if score >= 31 && score <= 40 {
spawnPipes()
}
}
Two problems with the "shiftedEnemies()". The first one is obvious, I cant write a code for every 10 score. The second problem is that this code doesn't even work. "SpawnEnemies()" is the only function that is shown. "spawnPipes()" doesn't show, ever. Maybe problem number two will be solved when I fix problem number 1.
Thx guys!
the reason your SpawnEnemies function is the only function that is being called is because you put the function shiftEnemies in the didMove(toView:) method and didMove(toView:) only gets called one time when you present your scene
what i recommend is try calling the function shiftEnemies() in the part of code where the score is being added (most likely in your didBeginContact method)
So you want to start by calling spawnenemies and when the score goes past a multiple of 10, switch to calling spawnPipes and then back to spawnEnemies at the next multiple of 10 etc?
Simply have a bool called shouldSpawnEnemies:
var shouldSpawnEnemies = true // Start by spawning enemies
(if you want to start by spawning pipes, initialise this to false).
Initialise the score at which functions should switch:
var switchFunctionScore = 10
Put a property watcher on your score. When the score passes the 'switch' score, set the bool indicating which function to use to false. Then set the next score at which functions should be switched.
var score : int = 0 {
didSet {
if (score >= switchFunctionScore) && oldValue < switchFunctionScore) {
shouldSpawnEnemies = !shouldSpawnEnemies
switchFunctionScore += 10
}
}
Then, whenever you need to call one of these functions; just check the value of shouldSpawnEnemies:
if shouldSpawnenemies {
spawnEnemies
} else {
spawnPipes
}
I would avoid using Timer, Timer works outside of the SpriteKit time system, so for me to cheat in your game, I could constantly exit and return the app, and since Timer is based on real time and not game time, the time that is spent outside of the game will still be accounted for.
What you want to do is use SKAction wait(duration:), 'sequence, 'run(_ block:) repeatForever and repeat(_:count)
To do this, you need to break it down into steps:
1st, we want to wait 1 second and fire function 1:
let wait1Sec = SKAction.wait(duration:1)
let function1 = SKAction.run({[weak self] in self?.function1()})
let seq1 = SKAction.sequence([wait1Sec,function1])
2nd, we want to create an action that repeats it 10 times:
let repeat1 = SKAction.repeat(seq1,count:10)
3rd, we want to do this again for function 2:
let function2 = SKAction.run({[weak self] in self?.function2()})
let seq2 = SKAction.sequence([wait1Sec,function2])
let repeat2 = SKAction.repeat(seq2,count:10)
Finally, we want to combine the 2 and run it indefinetely
let seqForever = SKAction.sequence([repeat1,repeat2])
let repeatForever = SKAction.repeatForever(seqForever)
Now that we have the action, we can attach it to the scene once
scene.run(repeatForever,withKey:"forever")
You now have a solution that will constantly fire a method 10 times in 10 seconds, then switch to the other function for 10 more times in 10 seconds, repeating forever.
override func didMove(to view:SKView)
{
let wait1Sec = SKAction.wait(duration:1)
let function1 = SKAction.run({[weak self] in self?.function1()})
let seq1 = SKAction.sequence([wait1Sec,function1])
let repeat1 = SKAction.repeat(seq1,count:10)
let function2 = SKAction.run({[weak self] in self?.function2()})
let seq2 = SKAction.sequence([wait1Sec,function2])
let repeat2 = SKAction.repeat(seq2,count:10)
let seqForever = SKAction.sequence([repeat1,repeat2])
let repeatForever = SKAction.repeatForever(seqForever)
scene.run(repeatForever,withKey:"forever")
}

How to make sprites follow a random pattern within a circle?

I am makeing a game in which I want that the enemies move following a random pattern within a circle. I already made that the enemies spawn randomly in all the sides of the screen, but the problem is that I dont know how to make the enemies move following a random pattern within a circle just like the image.
class GameScene: SKScene, SKPhysicsContactDelegate {
var circuloPrincipal = SKSpriteNode(imageNamed: "circulo")
var enemigoTimer = NSTimer()
}
override func didMoveToView(view: SKView) {
circuloPrincipal.size = CGSize(width: 225, height: 225)
circuloPrincipal.position = CGPoint(x: frame.width / 2, y: frame.height / 2)
circuloPrincipal.color = colorAzul
circuloPrincipal.colorBlendFactor = 1.0
circuloPrincipal.name = "circuloPrincipal"
circuloPrincipal.zPosition = 1.0
self.addChild(circuloPrincipal)
override func touchesBegan(touches: Set, withEvent event: UIEvent?) {
enemigoTimer = NSTimer.scheduledTimerWithTimeInterval(0.5, target: self, selector: Selector("enemigos"), userInfo: nil, repeats: true)
}
func enemigos() {
let enemigo = SKSpriteNode(imageNamed: "enemigo")
enemigo.size = CGSize(width: 25, height: 25)
enemigo.zPosition = 2.0
enemigo.name = "enemigo"
let posisionRandom = arc4random() % 4
switch posisionRandom {
case 0:
enemigo.position.x = 0
let posisionY = arc4random_uniform(UInt32(frame.size.height))
enemigo.position.y = CGFloat(posisionY)
self.addChild(enemigo)
break
case 1:
enemigo.position.y = 0
let posisionX = arc4random_uniform(UInt32(frame.size.width))
enemigo.position.x = CGFloat(posisionX)
self.addChild(enemigo)
break
case 2:
enemigo.position.y = frame.size.height
let posisionX = arc4random_uniform(UInt32(frame.size.width))
enemigo.position.x = CGFloat(posisionX)
self.addChild(enemigo)
break
case 3:
enemigo.position.x = frame.size.width
let posisionY = arc4random_uniform(UInt32(frame.size.height))
enemigo.position.y = CGFloat(posisionY)
self.addChild(enemigo)
break
default:
break
}
enemigo.runAction(SKAction.moveTo(circuloPrincipal.position, duration: 1.4))
}
Try to add this code:
let randomY = CGFloat(Int.random(-Int(circuloPrincipal.frame.height/2)...Int(circuloPrincipal.frame.height/2)))
let randomX = CGFloat(Int.random(-Int(circuloPrincipal.frame.width/2)...Int(circuloPrincipal.frame.width/2)))
let slopeToCirculoPrincipal = (enemigo.position.y - circuloPrincipal.position.y + randomY ) / (enemigo.position.x - circuloPrincipal.position.x + randomX)
let constant = enemigo.position.y - slopeToCirculoPrincipal * enemigo.position.x
let finalX : CGFloat = enemigo.position.x < circuloPrincipal.position.x ? 1500.0 : -1500.0 // Set it to somewhere outside screen size
let finalY = constant + slopeToCirculoPrincipal * finalX
let distance = (enemigo.position.y - finalY) * (enemigo.position.y - finalY) + (enemigo.position.x - finalX) * (enemigo.position.x - finalX)
let enemigoSpeed : CGFloat = 100.0
let timeToCoverDistance = sqrt(distance) / enemigoSpeed
let moveAction = SKAction.moveTo(CGPointMake(finalX, finalY), duration: NSTimeInterval(timeToCoverDistance))
let removeAction = SKAction.runBlock { () -> Void in
enemigo.removeFromParent()
}
enemigo.runAction(SKAction.sequence([moveAction,removeAction]))
Instead of:
enemigo.runAction(SKAction.moveTo(circuloPrincipal.position, duration: 1.4))
Also you need to put this extension somewhere in your project:
extension Int
{
static func random(range: Range<Int> ) -> Int
{
var offset = 0
if range.startIndex < 0 // allow negative ranges
{
offset = abs(range.startIndex)
}
let mini = UInt32(range.startIndex + offset)
let maxi = UInt32(range.endIndex + offset)
return Int(mini + arc4random_uniform(maxi - mini)) - offset
}
}

Spritekit - Increasing speed and keeping the same distance between sprite nodes?

I am having an issue where my platforms distance becomes messed up after a certain score has been reached. I want the game to start just like the spawnDelayForever part in the touchesBegan until the score reaches 5. After the score reaches 5…10…15 and so on, I want the platforms to be able to speed up with the same amount of space in between each spawn no matter what. The issue is when the score reaches 5, the platforms speed but the distance/space in between each spawn is not the same. They are further apart. Can anybody help me?
Hopefully this didn’t confuse anyone but if it did let me clarify it. I just want the platforms to speed up after a certain score has been reached with the same amount of space between each spawn. Meaning the waitForDuration should speed up (less than 2.0) with each speed increase.
Part of my code:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
if gameStarted == false {
gameStarted = true
ball.physicsBody?.affectedByGravity = true
title.removeFromParent()
let spawn = SKAction.runBlock({
()in
self.creatingPlatforms()
})
let delay = SKAction.waitForDuration(2.0)
let spawnDelay = SKAction.sequence([spawn, delay])
let spawnDelayForever = SKAction.repeatActionForever(spawnDelay)
self.runAction(spawnDelayForever)
let distance = CGFloat(self.frame.width + 170 + officialPlatform.frame.width)
let movePlatforms = SKAction.moveByX(-distance, y: 0, duration: NSTimeInterval(0.01 * distance))
let removePlatforms = SKAction.removeFromParent()
moveAndRemove = SKAction.sequence([movePlatforms, removePlatforms])
ball.physicsBody?.velocity = CGVectorMake(0, 0)
ball.physicsBody?.applyImpulse(CGVectorMake(0, 0))
scoreLbl.fontSize = 35
scoreLbl.position = CGPoint(x: self.frame.width / 2, y: scene!.frame.height / 2 + 300)
scoreLbl.fontColor = UIColor.blackColor()
scoreLbl.text = "\(score)"
score = 0
self.addChild(scoreLbl)
}
else {
ball.physicsBody?.velocity = CGVectorMake(0, 0)
ball.physicsBody?.applyImpulse(CGVectorMake(0, 45))
ball.physicsBody?.restitution = 0
scoreNode.physicsBody?.restitution = 0
}
}
func creatingPlatforms() {
scoreNode = SKSpriteNode()
scoreNode.size = CGSize(width: 120, height: 25)
scoreNode.position = CGPoint(x: self.frame.width + 95, y: self.frame.height / 2)
scoreNode.physicsBody = SKPhysicsBody(rectangleOfSize: scoreNode.size)
scoreNode.physicsBody?.affectedByGravity = false
scoreNode.physicsBody?.dynamic = false
scoreNode.physicsBody?.categoryBitMask = physicsCategory.score
scoreNode.physicsBody?.collisionBitMask = 0
scoreNode.physicsBody?.contactTestBitMask = physicsCategory.ball
scoreNode.name = "scoreNode"
officialPlatform = SKNode()
let platform = SKSpriteNode(imageNamed: "platform")
platform.size = CGSize(width: 140, height: 25)
platform.position = CGPoint(x: self.frame.width + 95, y: self.frame.height / 2)
platform.physicsBody = SKPhysicsBody(rectangleOfSize: platform.size)
platform.physicsBody?.categoryBitMask = physicsCategory.platform
platform.physicsBody?.collisionBitMask = physicsCategory.ball
platform.physicsBody?.contactTestBitMask = physicsCategory.ball
platform.physicsBody?.affectedByGravity = false
platform.physicsBody?.dynamic = false
officialPlatform.name = "official platform"
officialPlatform.runAction(moveAndRemove)
officialPlatform.addChild(platform)
officialPlatform.addChild(scoreNode)
self.addChild(officialPlatform)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if score >= 5 && score <= 9 {
moveAndRemove.speed = 0.9
}
}
}
I found my answer in another question on here.
swift - Increase speed of objects over time
I changed my self.runAction(spawnDelayForever) in my touchesBegan to self.runAction(spawnDelayForever, withKey: "spawnDelayForever"). Then in the update function, I changed it to:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if score > 0 {
self.actionForKey("spawnDelayForever")?.speed += 0.001
moveAndRemove.speed += 0.001
}
}