How would I repeat an action forever in Swift? - xcode

http://i.imgur.com/xkWTk9i.png I already got this rectangle to go from top to bottom. The problem I have is that I want it to repeat every 2 seconds so another rectangle is following it. I want my code to spawn the rectangles every 2 seconds and have it repeat like in flappy bird does with the green pipes. Thank you. (I got this to work before but I deleted my project by mistake and cant figure out how I did it in the first place.) Im in Swift using Spritekit.
.
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed: "Rectangle 12")
override func didMoveToView(view: SKView) {
self.addChild(sprite)
//run doAction function
doAction()
}
//movement of rectangle
func createRectangle() {
let moveToBottom = SKAction.moveByX(0, y: 0 - self.frame.size.width , duration:
NSTimeInterval (3.0))
let removeTheNode = SKAction.removeFromParent()
let moveAndRemovePipes = SKAction.sequence([moveToBottom, removeTheNode])
let repeatAction = SKAction.repeatActionForever(moveAndRemovePipes)
sprite.xScale = 1
sprite.yScale = 1
sprite.position = CGPoint(x:0,y:0)
sprite.runAction(repeatAction)
}
//spawn multiple rectangles after 3 or 4 seconds
func doAction() {
let generateRectangles = SKAction.sequence([
SKAction.runBlock(self.createRectangle),
SKAction.waitForDuration(NSTimeInterval(3.0))])
let endlessAction = SKAction.repeatActionForever(generateRectangles)
runAction(endlessAction)
}
}

You can repeat the function execution with NSTimer.
override func didMoveToView(view: SKView) {
self.addChild(sprite)
var timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: "doAction", userInfo: nil, repeats: true)
}
This will repeat your function execution for every 2 second.
EDIT :
You can do it this way too :
override func didMoveToView(view: SKView) {
self.addChild(sprite)
runAction(SKAction.repeatActionForever(SKAction.sequence([SKAction.runBlock(doAction), SKAction.waitForDuration(1.0)])))
}

run(SKAction.repeatForever(SKAction.sequence([SKAction.run(doAction), SKAction.wait(forDuration: 2.0)])))

Related

Run an SKSpriteNode object along a line drawn with the mouse in Swift

If you let go of the left mouse button, my SKSpriteNode object should move along the line previously "drawn" with the mouse.
My problem is that my sprite node is moving down when I release the left mouse button.
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
let object: SKSpriteNode = SKSpriteNode(imageNamed: "rabbit")
var actionArray = [SKAction()]
override func didMove(to view: SKView) {
physicsWorld.gravity = CGVector(dx: 0, dy: 0)
physicsWorld.contactDelegate = self
object.setScale(0.2)
object.position = CGPoint(x: size.width / 2, y: size.height / 2)
object.zPosition = 0
object.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "rabbit"), size: object.size)
object.physicsBody?.mass = 0
object.physicsBody?.categoryBitMask = 0
object.physicsBody?.contactTestBitMask = 1
object.physicsBody?.collisionBitMask = 0
addChild(object)
}
override func mouseDragged(with event: NSEvent) {
actionArray.append(SKAction.move(to: event.location(in: self), duration: 1))
}
override func mouseUp(with event: NSEvent) {
object.run(SKAction.sequence(actionArray))
// Remove all actions from 'actionArray'.
actionArray.removeAll()
}
}
I have already tried it as shown below. Now my sprite node moves to the first point, where I pressed down the left mouse button.
override func mouseUp(with event: NSEvent) {
let removeAllFromActionArray = SKAction.run {
self.actionArray.removeAll()
}
actionArray.append(removeAllFromActionArray)
object.run(SKAction.sequence(actionArray))
}
I've already tried view! .Convert (event.locationInWindow, to: view! .Scene!) Instead of event.location (in: self), as indicated in Position of mouse click relative to scene, not window, but the result was the same.
I suspect that the bug lies in override func mouseUp(with event: event).
Where does the error lie or is there another solution?

SpriteKit animate nodes to specific position

I am building my first game with SpriteKit, written in Swift 3.
I have been trying for hours to accomplish this "winning stars effect", but without any acceptable result yet :/
The effect i am looking for is demonstrated in this video: https://youtu.be/9CeK5_G8T9E
It's not a problem adding one or multiple stars; the problem is to make them move in different directions by different paths to the same location, just like it's done in this video example.
Hope for your help to lead me in the right direction.
Any solution, tutorials, examples etc. is more than welcome.
Thanks for your time.
Took a while but here's something:
import SpriteKit
class GameScene: SKScene {
var sprite = SKSpriteNode()
override func didMove(to view: SKView) {
for _ in 1...15 {
spawnsprite()
}
}
func spawnsprite() {
sprite = SKSpriteNode(color: SKColor.yellow, size: CGSize(width: 50, height: 50))
sprite.position = CGPoint(x: (-size.width/2)+50, y: (-size.height/2)+50)
addChild(sprite)
let destination = CGPoint(x: (size.width/2)-50, y: (size.height/2)-50)
let path = createCurvedPath(from: sprite.position, to: destination, varyingBy: 500)
let squareSpeed = CGFloat(arc4random_uniform(600)) + 200
let moveAction = SKAction.follow(path, asOffset: false, orientToPath: false, speed: squareSpeed)
let rotateAction = SKAction.repeatForever(SKAction.rotate(byAngle: 2 * CGFloat.pi, duration: 2))
sprite.run(SKAction.group([moveAction, rotateAction]))
}
func createCurvedPath(from start: CGPoint, to destination: CGPoint, varyingBy offset: UInt32) -> CGMutablePath {
let pathToMove = CGMutablePath()
pathToMove.move(to: start)
let controlPoint = CGPoint(x: CGFloat(arc4random_uniform(offset)) - CGFloat(offset/2),
y: CGFloat(arc4random_uniform(offset)) - CGFloat(offset/2))
pathToMove.addQuadCurve(to: destination, control: controlPoint)
return pathToMove
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
There's a lot that can be done to make this nicer (e.g. subclass SKSpriteNode and make the path that the ode follows a property of the sprite itself), but it illustrates the principle.
It could probably also be done with an SKEmitter.

SKScene and Swift Files are not linking

In my xcode project I am trying to transition from one SKScene to another. What triggers the transition is the touching of a SKLabelNode. All of that is working correctly. But after the scene changes none of my code from my class that controls the "StartScence.sks" works. It seems as if my "StartScene.swift" and my "StartScene.sks" are not linked.
This is the code in my GameScene,
import SpriteKit
class GameScene: SKScene {
var isTouched: Bool = false
var booleanTouched: Bool!
let ns = SKScene(fileNamed: "StartScene")
let crosswf = SKTransition.crossFadeWithDuration(2)
override func didMoveToView(view: SKView) {
let backgroundimage = SKSpriteNode(imageNamed: "ipbg")
backgroundimage.size = CGSize(width: self.frame.size.width, height: self.frame.size.height)
backgroundimage.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2)
addChild(backgroundimage)
let playButton = SKLabelNode(fontNamed: "")
playButton.name = "play"
playButton.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height / 2 + 100)
playButton.text = "Play"
let wait = SKAction.waitForDuration(2)
let run = SKAction.runBlock({
let randomNumber = Int(arc4random_uniform(UInt32(4)))
switch(randomNumber){
case (0):
playButton.fontColor = UIColor.blueColor()
case 1:
playButton.fontColor = UIColor.yellowColor()
case 2:
playButton.fontColor = UIColor.purpleColor()
case 3:
playButton.fontColor = UIColor.orangeColor()
default: print("default")
}
})
addChild(playButton)
var repeatActionForever = SKAction.repeatActionForever(SKAction.sequence([wait, run]))
runAction(repeatActionForever)
backgroundimage.zPosition = 1
playButton.zPosition = 2
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first! as UITouch
let touchLocation = touch.locationInNode(self)
let touchedNode = nodeAtPoint(touchLocation)
if (touchedNode.name == "play"){
scene!.view?.presentScene(ns!, transition: crosswf)
}else{
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
And this is the code that is my "StartScene.swift" that isnt controlling the "StartScene.sks" properly.
import SpriteKit
class StartScene: SKScene {
override func didMoveToView(view: SKView) {
print("Scene Loaded")
}
}
There's two things to be aware of.
In your code you are currently loading your .SKS file like this
let ns = SKScene(fileNamed: "StartScene")
Your new scene will load, as all SKS Files are of the class SKScene.
But, it will only use code from that class.
If you want it to load with the code in your class StartScene, a subclass of SKScene. Change the line to this
let ns = StartScene(fileNamed: "StartScene")
We can also make the SKS File have a custom class instead of it's default SKScene. So when it's loaded it uses a custom class.
Open the SKS File in Xcode so you can give the scene a Custom Class. In the scene editor and with nothing selected. Click in the utilities area, switch to the Custom Class Inspector, which is the last tab on the right.
Give it a Custom Class of StartScene.
It should work.

How can I run Score Label when I touch the object?

I added Score Label in my game. But It doesn't run. When I touch the ball, Score should be increase. I couldn't connect ball with score label. Can anyone help me? Here is my code :
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
let borderBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
physicsWorld.gravity = CGVectorMake(5, 5)
let ball = childNodeWithName(BallCategoryName) as! SKSpriteNode
ball.physicsBody!.applyImpulse(CGVectorMake(10, -10))
scoreLabel.fontName = "Helvetica Neue Light"
scoreLabel.text = "Score: 0"
scoreLabel.fontSize = 40
scoreLabel.color = SKColor.redColor()
scoreLabel.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
scoreLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.Top
scoreLabel.position = CGPoint(x: 250, y: size.height - 100)
addChild(scoreLabel)
}
And there is touch code :
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch:AnyObject in touches{
let location = (touch as! UITouch).locationInNode(self)
let nodeTouched = nodeAtPoint(location)
let ball = childNodeWithName(BallCategoryName) as! SKSpriteNode
//Detect if the ball is touched
if(nodeTouched == ball){
physicsWorld.gravity = CGVectorMake(-30, 30)
}
}
Try this instead:
if ((nodeTouched.name?.containsString(BallCategoryName)) != nil) {
//Update Score
}
Keep in mind that we don't know exactly what BallCategoryName is or how you set it up, so you have to make sure it isn't nil.
Also, if your current method in touchesBegan is running your physicsWorld.gravity = CGVectorMake(-30, 30) line successfully, then we would know that something is wrong with your scoreLabel property and not the touch detection method.
If it was me, I would just make ball a property of your scene and then test it like so in touchesBegan:
if self.ball.containsPoint(touchLocation) {
//Update Score
}

Keep getting thread 1, can't rotate a ball in Swift

I have tried many ways of writing the code to get it to rotate, but keep getting threads.
First way:
import UIKit
import SpriteKit
class PlayScene: SKScene {
let hero = SKSpriteNode(imageNamed: "hero.png")
let groundSpeed = 5
override func didMoveToView(view: SKView) {
// code
}
override func update(currentTime: NSTimeInterval) {
//rotate the hero (in shape of a ball)
let rotateAction = SKAction.rotateByAngle(5, duration: 1)
let repeatAction = SKAction.repeatActionForever(rotateAction)
hero.runAction(repeatAction)
}
}
Second way:
var degreeRotation = CDouble(self.groundSpeed) * M_PI / 180
//rotate the hero
self.hero.zRotation -= CGFloat(degreeRotation)
I have tried making an Int extension for degree to radians translation (like someone already suggested on Stackoverflow) but for some reason none of them worked. I am very new to Swift and I don't know what to try anymore, so if you can help me, please do.
Change your code to this...
import UIKit
import SpriteKit
class PlayScene: SKScene {
let hero = SKSpriteNode(imageNamed: "hero.png")
let groundSpeed = 5
override func didMoveToView(view: SKView) {
// code to add the hero sprite to the view.
// if the sprite is not added to the view then nothing will happen with actions.
// M_PI = half a turn. Work in radians, not degrees.
let rotateAction = SKAction.rotateByAngle((CGFloat)M_PI, duration: (NSTimeInterval)1)
let repeatAction = SKAction.repeatActionForever(rotateAction)
hero.runAction(repeatAction)
}
}

Resources