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)
}
}
Related
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.
Modus Operandi:
1) Use an UIImageView of a base Clock Image.
2) Add MinuteHand & HourHand sublayers (containing their respective images) to the UIImageView layer.
Problem: both sublayers disappear when attempting to perform a rotation transformation.
Note: 1) I've removed the 'hour' code & ancillary radian calculations to simplify code.
2) The 'center' is the center of the clock. I had adjusted the coordinates to actually pin the hands to the clock's center.
3) The ViewDidLayoutSubviews() appear to be okay. I got the clock + hands.
class ClockViewController:UIViewController {
private let minuteLayer = CALayer()
#IBOutlet weak var clockBaseImageView: UIImageView!
#IBOutlet weak var datePicker: UIDatePicker!
override func viewDidLayoutSubviews() {
guard var minuteSize = UIImage(named: "MinuteHand")?.size,
var hourSize = UIImage(named: "HourHand")?.size
else {
return
}
var contentLayer:CALayer {
return self.view.layer
}
var center = clockBaseImageView.center
// Minute Hand:
minuteLayer.setValue("*** Minute Hand ***", forKey: "id")
minuteSize = CGSize(width: minuteSize.width/3, height: minuteSize.height/3)
minuteLayer.contents = UIImage(named: "MinuteHand")?.cgImage
center = CGPoint(x: 107.0, y: 40.0)
var handFrame = CGRect(origin: center, size: minuteSize)
minuteLayer.frame = handFrame
minuteLayer.contentsScale = clockBaseImageView.layer.contentsScale
minuteLayer.anchorPoint = center
clockBaseImageView.layer.addSublayer(minuteLayer)
}
Here's my problem: Attempting to rotate the minute hand via 0.01 radians:
func set(_ time:Date) {
minuteLayer.setAffineTransform(CGAffineTransform(rotationAngle: .01)) // random value for test.
}
Before rotation attempt:
After attempting to rotate minute hand:
The hand shifted laterally to the right vs rotate.
Why? Perhaps due to the pivot point?
I think this will solve your problem, Take a look and let me know.
import GLKit // Importing GLKit Framework
func set(_ time:Date) {
minuteLayer.transform = CGAffineTransformMakeRotation(CGFloat(GLKMathDegreesToRadians(0.01)))
}
Note: this solution doesn't solve the issue about rotating a CALayer. Instead, it bypasses the issue by replacing the layer with a subview and rotating the subview via:
func set(_ time:Date) {
minuteView.transform = CGAffineTransform(rotationAngle: 45 * CGFloat(M_PI)/180.0)
}
Here's the result:
Still, it would be nice to know how to rotate a CALayer.
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.
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)])))
Does anyone know what the code would be to make a background image that would cover the whole GameScene.swift
This is my code atm -
import SpriteKit
class GameScene: SKScene {
let playbutton = SKSpriteNode (imageNamed: "play")
let score = SKSpriteNode (imageNamed: "score")
var background = SKSpriteNode (imageNamed: "background")
override func didMoveToView(view: SKView) {
func loadBackGround()
{
background = SKSpriteNode(imageNamed: "background")
background.name = "background"
background.zPosition = 1.0
background.size = self.scene.size
scene.addChild(background)
}
self.playbutton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(self.playbutton)
self.score.position = CGPointMake(CGRectGetMidX(self.frame), 100)
self.addChild(self.score)
You may have to be more specific (iOS Game, Mac Game...), but I think something like this should work:
var bgImage:SKSpriteNode
func loadBackGround()
{
bgImage = SKSpriteNode(imageNamed: "BACKGROUND IMAGE NAME")
bgImage.name = "bg"
bg.size = self.scene.size
scene.addChild(bg)
}
(you can use it outside of a function of course)
EDIT: Try this code:
import SpriteKit
class GameScene: SKScene {
let playbutton = SKSpriteNode (imageNamed: "play")
let score = SKSpriteNode (imageNamed: "score")
var background = SKSpriteNode ()
func loadBackGround() // You define the function and what it does
{
background = SKSpriteNode(imageNamed: "background")
background.name = "background"
background.size = self.scene.size
scene.addChild(background)
}
override func didMoveToView(view: SKView) {
loadBackGround() // You call the function you defined
self.playbutton.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame))
self.addChild(self.playbutton)
self.score.position = CGPointMake(CGRectGetMidX(self.frame), 100)
self.addChild(self.score)
}
This is the way I use:
override init(size: CGSize) {
super.init(size: size)
anchorPoint = CGPoint(x: 0.5, y: 0.5)
let background = SKSpriteNode(imageNamed: "Background")
addChild(background)
}
This loads the background image from the asset catalog and places it in the scene. Because the scene’s anchorPoint is (0.5, 0.5), the background image will always be centered on the screen on both 3.5-inch and 4-inch devices.
I get this from a good tutorial: How to Make a Game Like Candy Crush with Swift Tutorial: Part 1