Create HUD in Sprite Kit that remains static on screen - ios

I'm trying to create a static HUD that stays at the top of the screen throughout the game in a jump game from this tutorial. I'm still new to Swift, so maybe it's a convention I don't see easily:
http://www.raywenderlich.com/87232/make-game-like-mega-jump-sprite-kit-swift-part-2
In the tutorial, he puts the HUD in the override init(size: CGSize) function:
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(size: CGSize) {
super.init(size: size)
...
// Build the HUD
// HUD
hudNode = SKNode()
hudNode.zPosition = 1000
addChild(hudNode)
// Coins
// 1
let coin = SKSpriteNode(imageNamed: "powerup05_1")
coin.position = CGPoint(x: 300, y: self.size.height-100)
coin.zPosition = 1000
hudNode.addChild(coin)
// 2
lblCoins = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblCoins.fontSize = 70
lblCoins.fontColor = SKColor.whiteColor()
lblCoins.position = CGPoint(x: 375, y: self.size.height-100)
lblCoins.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
lblCoins.zPosition = 1000
// 3
lblCoins.text = String(format: "X %d", GameState.sharedInstance.coins)
hudNode.addChild(lblCoins)
// Score
// 4
lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblScore.fontSize = 70
lblScore.fontColor = SKColor.whiteColor()
lblScore.position = CGPoint(x: self.size.width-325, y: self.size.height-100)
lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Right
lblScore.zPosition = 1000
// 5
lblScore.text = "0"
hudNode.addChild(lblScore)
}
However, if I try to use this function in my game I get an error:
Argument labels '(fileNamed:)' do not match any available overloads
In this function:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
switch gameState.currentState {
case is WaitingForTap:
gameState.enterState(WaitingForBomb)
// Switch to playing state
self.runAction(SKAction.waitForDuration(2.0),
completion:{
self.gameState.enterState(Playing)
})
case is GameOver:
// Error! Argument labels '(fileNamed:)' do not match any available overloads
let newScene = GameScene(fileNamed:"GameScene")
newScene!.scaleMode = .AspectFill
let reveal = SKTransition.flipHorizontalWithDuration(0.5)
self.view?.presentScene(newScene!, transition: reveal)
default:
break
}
}
So I've resorted to putting it in the override func didMoveToView(view: SKView), but the HUD moves once the player jumps up:
override func didMoveToView(view: SKView) {
....
// Build the HUD
// HUD
hudNode = SKNode()
hudNode.zPosition = 1000
addChild(hudNode)
// Coins
// 1
let coin = SKSpriteNode(imageNamed: "powerup05_1")
coin.position = CGPoint(x: 300, y: self.size.height-100)
coin.zPosition = 1000
hudNode.addChild(coin)
// 2
lblCoins = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblCoins.fontSize = 70
lblCoins.fontColor = SKColor.whiteColor()
lblCoins.position = CGPoint(x: 375, y: self.size.height-100)
lblCoins.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Left
lblCoins.zPosition = 1000
// 3
lblCoins.text = String(format: "X %d", GameState.sharedInstance.coins)
hudNode.addChild(lblCoins)
// Score
// 4
lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblScore.fontSize = 70
lblScore.fontColor = SKColor.whiteColor()
lblScore.position = CGPoint(x: self.size.width-325, y: self.size.height-100)
lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.Right
lblScore.zPosition = 1000
// 5
lblScore.text = "0"
hudNode.addChild(lblScore)
}

Adding your hudNode to the cameraNode makes the HUD move together with the camera so that it will look static on screen. That is to change addChild(hudNode) to cameraNode.addChild(hudNode).
Then use convert(_:to:) to change the point from the current scene coordinate to the cameraNode coordinate. That will set HUD's position in the new coordinate with just a little modification. For instance, change coin.position like this:
coin.position = convert(CGPoint(x: 300, y: self.size.height-100), to: cameraNode)
Do the same thing to the other HUD:
lblCoins.position = convert(CGPoint(x: 375, y: self.size.height-100), to: cameraNode)
lblScore.position = convert(CGPoint(x: self.size.width-325, y: self.size.height-100), to: cameraNode)
The result after those 4 lines modification:

Related

TouchesBegan does not work

In the last label lblTryAgain I want to return to another class named GameScene but the tap action does not enter to the function touchesBegan.
I am just following a tutorial to learn how to create games with SpriteKit if someone want to follow the tutorial or see the entire code is available in https://www.raywenderlich.com/87231/make-game-like-mega-jump-sprite-kit-swift-part-1
import SpriteKit
class EndGameScene: SKScene {
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(size: CGSize) {
super.init(size: size)
// Stars
let star = SKSpriteNode(imageNamed: "Star")
star.position = CGPoint(x: 25, y: self.size.height-30)
addChild(star)
let lblStars = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblStars.fontSize = 30
lblStars.fontColor = SKColor.white
lblStars.position = CGPoint(x: 50, y: self.size.height-40)
lblStars.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.left
lblStars.text = String(format: "X %d", GameState.sharedInstance.stars)
addChild(lblStars)
// Score
let lblScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblScore.fontSize = 60
lblScore.fontColor = SKColor.white
lblScore.position = CGPoint(x: self.size.width / 2, y: 300)
lblScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center
lblScore.text = String(format: "%d", GameState.sharedInstance.score)
addChild(lblScore)
// High Score
let lblHighScore = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblHighScore.fontSize = 30
lblHighScore.fontColor = SKColor.cyan
lblHighScore.position = CGPoint(x: self.size.width / 2, y: 150)
lblHighScore.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center
lblHighScore.text = String(format: "High Score: %d", GameState.sharedInstance.highScore)
addChild(lblHighScore)
// Try again
let lblTryAgain = SKLabelNode(fontNamed: "ChalkboardSE-Bold")
lblTryAgain.fontSize = 30
lblTryAgain.fontColor = SKColor.white
lblTryAgain.position = CGPoint(x: self.size.width / 2, y: 50)
lblTryAgain.horizontalAlignmentMode = SKLabelHorizontalAlignmentMode.center
lblTryAgain.text = "Tap To Try Again"
lblTryAgain.isUserInteractionEnabled = true
addChild(lblTryAgain)
}
func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
// Transition back to the Game
let reveal = SKTransition.fade(withDuration: 0.5)
let gameScene = GameScene(size: self.size)
self.view!.presentScene(gameScene, transition: reveal)
}
}
touchesBegan is a override func which only works in self.viewand it will not work in other UIs
try this
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
print("working")
}
make sure isUserInteractionEnabled is checked and the print statement may not work. add or update sceneDidLoad and print isUserInteractionEnabled

No sound without any errors

I tried to add sound when button pressed but when I press the button no sound. No mistakes in terminal.
I already tried compile on another computer, and debug on iPhone.
No sound persist.
Please help me solve this problem.
class MenuScene : SKScene {
let txtAtlas : SKTextureAtlas = SKTextureAtlas(named: "UI.atlas")
let txtAtlas2 : SKTextureAtlas = SKTextureAtlas(named: "mapbc.atlas")
let startBut = SKSpriteNode()
let tssound = SKAction.playSoundFileNamed("tssound.wav", waitForCompletion: false)
override func didMove(to view: SKView) {
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.backgroundColor = UIColor.black
let logo = SKLabelNode(fontNamed: "Helvetica")
logo.text = "Train Control"
logo.position = CGPoint(x: 0, y: 100)
logo.fontSize = 60
logo.fontColor = UIColor.green
self.addChild(logo)
startBut.texture = txtAtlas.textureNamed("stbut.png")
startBut.size = CGSize(width: 230, height: 85)
startBut.position = CGPoint(x: 0, y: -20)
startBut.name = "StartBtn"
self.addChild(startBut)
let startText = SKLabelNode(fontNamed: "AvenirNext-HeavyItalic")
startText.text = "Start Manage!"
startText.verticalAlignmentMode = .center
startText.position = CGPoint(x: 0, y: 2)
startText.fontSize = 30
startText.name = "StartBtn"
startBut.addChild(startText)
let bcmap = SKSpriteNode()
bcmap.texture = txtAtlas2.textureNamed("mapbc.png")
bcmap.zPosition = -1
bcmap.size = CGSize(width: 400.0, height: 350.0)
bcmap.position = CGPoint(x: -100, y: 0)
bcmap.alpha = 0.7
self.addChild(bcmap)
let stbutaction = SKAction.sequence([SKAction.fadeAlpha(to: 0.7, duration: 0.9),
SKAction.fadeAlpha(to: 1.0, duration: 0.9)])
startBut.run(SKAction.repeatForever(stbutaction))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let location = touch.location(in: self)
let nodeTouched = atPoint(location)
if nodeTouched.name == "StartBtn" {
// Player touched the start text or button node
self.view?.presentScene(GameScene(size: self.size))
self.run(tssound)
}
}
}
}
You should probably initialise and run your sound inside GameScene e.g. when the scene loads. You are trying to run the sound on MenuScene which doesn't exist after you have transitioned to GameScene

share menu opens at twice rather just when button is pressed

I have added a share menu to my game, when the game is over the game over pop up appears with a share menu. when i press anywhere on the screen the share menu pops up and the game goes back to the beginning prompting the player to Tap to play again. Then when the player taps on the screen the share menu pops up again which makes the game unplayable after the first play through. I think i need to set the share menu to pop up only when SKSpriteNode for the share button is pressed, if the player taps anywhere else on the screen the game should just reset. Would be grateful if you could take a look at code and see where i am going wrong.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var shareButton = SKSpriteNode()
var santa = SKSpriteNode()
var bg = SKSpriteNode()
var scoreLabel = SKLabelNode()
var tapToPlayLabel = SKLabelNode()
var score = 0
var gameOverScreen = SKSpriteNode()
var timer = Timer()
enum ColliderType: UInt32 {
case santa = 1
case Object = 2
case Gap = 4
}
enum ButtonName: String {
case play
case share
}
var gameOver = false
func makeBlocks() {
let moveBlocks = SKAction.move(by: CGVector(dx: -2 * self.frame.width, dy: 0), duration: TimeInterval(self.frame.width / 100))
let gapHeight = santa.size.height * 4
let movementAmount = arc4random() % UInt32(self.frame.height / 2)
let blockOffset = CGFloat(movementAmount) - self.frame.height / 4
let blockTexture = SKTexture(imageNamed: "block1.png")
let block1 = SKSpriteNode(texture: blockTexture)
block1.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + blockTexture.size().height / 2 + gapHeight / 2 + blockOffset)
block1.run(moveBlocks)
block1.physicsBody = SKPhysicsBody(rectangleOf: blockTexture.size())
block1.physicsBody!.isDynamic = false
block1.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
block1.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
block1.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
block1.zPosition = -2
self.addChild(block1)
let block2Texture = SKTexture(imageNamed: "block2.png")
let block2 = SKSpriteNode(texture: block2Texture)
block2.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY - block2Texture.size().height / 2 - gapHeight / 2 + blockOffset)
block2.run(moveBlocks)
block2.physicsBody = SKPhysicsBody(rectangleOf: blockTexture.size())
block2.physicsBody!.isDynamic = false
block2.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
block2.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
block2.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
block2.zPosition = -2
self.addChild(block2)
let gap = SKNode()
gap.position = CGPoint(x: self.frame.midX + self.frame.width, y: self.frame.midY + blockOffset)
gap.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: blockTexture.size().width, height: gapHeight))
gap.physicsBody!.isDynamic = false
gap.run(moveBlocks)
gap.physicsBody!.contactTestBitMask = ColliderType.santa.rawValue
gap.physicsBody!.categoryBitMask = ColliderType.Gap.rawValue
gap.physicsBody!.collisionBitMask = ColliderType.Gap.rawValue
self.addChild(gap)
}
func didBegin(_ contact: SKPhysicsContact) {
if gameOver == false {
if contact.bodyA.categoryBitMask == ColliderType.Gap.rawValue || contact.bodyB.categoryBitMask == ColliderType.Gap.rawValue {
score += 1
scoreLabel.text = String(score)
} else {
self.speed = 0
gameOver = true
timer.invalidate()
let gameOverScreenTexture = SKTexture(imageNamed: "GameOverPopup.jpg")
var j: CGFloat = 0
gameOverScreen = SKSpriteNode(texture: gameOverScreenTexture, size: CGSize(width: 600, height: 600))
gameOverScreen.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
gameOverScreen.size.height = self.frame.height / 3
gameOverScreen.zPosition = -1
self.addChild(gameOverScreen)
//share button
let shareButtonTexture = SKTexture(imageNamed: "shareButton.png")
var k: CGFloat = 0
shareButton = SKSpriteNode(texture: shareButtonTexture, size: CGSize(width: 500, height: 100))
shareButton.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
shareButton.size.height = self.frame.height / 10
shareButton.name = "shareButton"
shareButton.zPosition = 0
self.addChild(shareButton)
}
}
}
func openShareMenu(value: String, image: UIImage?) {
guard let view = view else { return }
// Activity items
var activityItems = [AnyObject]()
// Text
let text = "Can you beat my score "
activityItems.append(text as AnyObject)
// Add image if valid
if let image = image {
activityItems.append(image)
}
// Activity controller
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
// Excluded activity types
activityController.excludedActivityTypes = [
UIActivityType.airDrop,
UIActivityType.print,
UIActivityType.assignToContact,
UIActivityType.addToReadingList,
]
// Present
view.window?.rootViewController?.present(activityController, animated: true)
}
override func didMove(to view: SKView) {
addChild(shareButton)
self.physicsWorld.contactDelegate = self
setupGame()
}
func setupGame() {
timer = Timer.scheduledTimer(timeInterval: 3, target: self, selector: #selector(self.makeBlocks), userInfo: nil, repeats: true)
let bgTexture = SKTexture(imageNamed: "bg.png")
let moveBGAnimation = SKAction.move(by: CGVector(dx: -bgTexture.size().width, dy: 0), duration: 7)
let shiftBGAnimation = SKAction.move(by: CGVector(dx: bgTexture.size().width, dy: 0), duration: 0)
let moveBGForever = SKAction.repeatForever(SKAction.sequence([moveBGAnimation, shiftBGAnimation]))
var i: CGFloat = 0
while i < 3 {
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: bgTexture.size().width * i, y: self.frame.midY)
bg.size.height = self.frame.height
bg.run(moveBGForever)
bg.zPosition = -3
self.addChild(bg)
i += 1
}
let santaTexture = SKTexture(imageNamed: "santa1.png")
let santaTexture2 = SKTexture(imageNamed: "santa2.png")
let animation = SKAction.animate(with: [santaTexture, santaTexture2], timePerFrame: 0.1)
let makeSantaMove = SKAction.repeatForever(animation)
santa = SKSpriteNode(texture: santaTexture)
santa.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
santa.run(makeSantaMove)
santa.physicsBody = SKPhysicsBody(circleOfRadius: santaTexture.size().height / 2)
santa.physicsBody!.isDynamic = false
santa.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
santa.physicsBody!.categoryBitMask = ColliderType.santa.rawValue
santa.physicsBody!.collisionBitMask = ColliderType.santa.rawValue
self.addChild(santa)
let ground = SKNode()
ground.position = CGPoint(x: self.frame.midX, y: -self.frame.height / 2)
ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.width, height: 1))
ground.physicsBody!.isDynamic = false
ground.physicsBody!.contactTestBitMask = ColliderType.Object.rawValue
ground.physicsBody!.categoryBitMask = ColliderType.Object.rawValue
ground.physicsBody!.collisionBitMask = ColliderType.Object.rawValue
self.addChild(ground)
scoreLabel.fontName = "Helvetica"
scoreLabel.fontSize = 60
scoreLabel.text = "0"
scoreLabel.position = CGPoint(x: self.frame.midX, y: self.frame.height / 2 - 220)
self.addChild(scoreLabel)
tapToPlayLabel.fontName = "Helvetica"
tapToPlayLabel.fontSize = 70
tapToPlayLabel.text = "Tap to Play!"
tapToPlayLabel.position = CGPoint(x: self.frame.midX, y: self.frame.height / 2 - 500)
self.addChild(tapToPlayLabel)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if gameOver == false {
tapToPlayLabel.isHidden = true
santa.physicsBody!.isDynamic = true
santa.physicsBody!.velocity = CGVector(dx: 0, dy: 0)
santa.physicsBody!.applyImpulse(CGVector(dx: 0, dy: 320))
} else {
tapToPlayLabel.isHidden = false
gameOver = false
score = 0
self.speed = 1
self.removeAllChildren()
setupGame()
}
if let name = shareButton.name {
if name == "shareButton" {
openShareMenu(value: "\(self.score)", image: nil)
}
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
You are calling the touches method wrong. You are not using the name of the touched node to see if it is the share button. You merely assign a property (if let name = ...) and see if its the share button. You need to compare with the name of the actual node that is touched. You are also running your gameOver code anytime touchesBegan fires, you need to differentiate.
Try this
override open func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self) // get location of touch
let nodeTouched = atPoint(location) // get touched node
// Pressed button
if let nodeName = nodeTouched.name {
switch nodeName {
case ButtonName.share.rawValue:
openShareMenu(value: "\(self.score)", image: nil)
return // return early if share button is pressed so your gameOver code below etc doesnt fire.
default:
break
}
}
// Did not press button, usual code
if gameOver == false {..... }
}
}
Hope this helps

How do I move one node from one position to another position?

I have a file where I move a square from one side to a point near the middle of the screen.
After that, I want to move it to another position, but I am unsure of how to do that.
This is my code so far. When you run it, it moves to one point and then stops.
What I want it to do is it moves to that one point and immediately goes to a point with the same x value but a y value touching the top of the screen.
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
createEnemies()
}
deinit{
print("deinit called")
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
}
//Helper method for spawning a point along the screen borders. This will not work for diagonal lines.
func randomPointBetween(start:CGPoint, end:CGPoint)->CGPoint{
return CGPoint(x: randomBetweenNumbers(start.x, secondNum: end.x), y: randomBetweenNumbers(start.y, secondNum: end.y))
}
func createEnemies(){
//Randomize spawning time.
//This will create a node every 0.5 +/- 0.1 seconds, means between 0.4 and 0.6 sec
let wait = SKAction .waitForDuration(0.5, withRange: 0.2)
weak var weakSelf = self //Use weakSelf to break a possible strong reference cycle
let spawn = SKAction.runBlock({
var random = arc4random() % 4 + 1
var position = CGPoint()
var moveTo = CGPoint()
var offset:CGFloat = 40
switch random {
//Left
case 1:
position = weakSelf!.randomPointBetween(CGPoint(x: 0, y: weakSelf!.frame.height/2), end: CGPoint(x: 0, y: weakSelf!.frame.height/2))
//Move to opposite side
moveTo = CGPoint(x: weakSelf!.frame.width/4, y: weakSelf!.frame.height/2)
//moveTo2 = CGPoint(x: weakSelf!.frame.width/4, y: weakSelf!.frame.height)
break
default:
break
}
weakSelf!.spawnEnemyAtPosition(position, moveTo: moveTo)
})
let spawning = SKAction.sequence([wait,spawn])
self.runAction(SKAction.repeatActionForever(spawning), withKey:"spawning")
}
func spawnEnemyAtPosition(position:CGPoint, moveTo:CGPoint){
let enemy = SKSpriteNode(color: SKColor.redColor(), size: CGSize(width: 40, height: 40))
enemy.position = position
enemy.physicsBody = SKPhysicsBody(rectangleOfSize: enemy.size)
enemy.physicsBody?.affectedByGravity = false
enemy.physicsBody?.dynamic = true
enemy.physicsBody?.collisionBitMask = 0 // no collisions
//Here you can randomize the value of duration parameter to change the speed of a node
let move = SKAction.moveTo(moveTo,duration: 2.5)
//let remove = SKAction.removeFromParent()
enemy.runAction(SKAction.sequence([move]))
/*if enemy.position = CGPoint(x: weakSelf!.frame.width/4, y: weakSelf!.frame.height/2) {
let move2 = SKAction.moveTo(moveTo2,duration: 2.5)
enemy.runAction(SKAction.sequence([move2]))
}*/
self.addChild(enemy)
print("\(enemy.position)")
}
func didBeginContact(contact: SKPhysicsContact) {
}
/*
Added for debugging purposes
override func touchesBegan(touches: NSSet, withEvent event: UIEvent?) {
//Just make a transition to the other scene, in order to check if deinit is called
//You have to make a new scene ... I named it WelcomeScene
var scene:WelcomeScene = WelcomeScene(fileNamed: "WelcomeScene.sks")
scene.scaleMode = .AspectFill
self.view?.presentScene(scene )
}
*/
Run a sequence for example.
moveTo = CGPoint(x: weakSelf!.frame.width/4, y: weakSelf!.frame.height/2)
moveTo2 = CGPoint(x: weakSelf!.frame.width/4, y: weakSelf!.frame.height)
let move = SKAction.moveTo(moveTo,duration: 2.5)
let move2 = SKAction.moveTo(moveTo2,duration: 2.5)
let moveToSequence = SKAction.sequence([move, move2])
enemy.runAction(moveToSequence)

Different sprite position with the same code

I have a weird issue with my sprite position, I tried clean build, restart xcode, and run in different schemes(iPhone5s, iPhone6), they all return the same strange issue.
I tried to set the position of the sprite by:
balls.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)
so when I println(balls.position) to the console, it returns (0.0, 0.0)
But when I tried
println(CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)
it returns (512.0,1408.0), and this is correct position where the ball should be.
I'm having issue with the last function, func ballPosition, it is used to determind the position of the sprite "balls". for some reason it is always (0, 0).
Here are the complete code from my test project:
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var circle = SKShapeNode()
var balls = SKShapeNode()
var ballColor = ["red", "blue", "green", "yellow"]
var points = ["up", "down", "left", "right"]
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
// set circle position and size
circle = SKShapeNode(circleOfRadius: 100 ) // Size of Circle
circle.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)) //Middle of Screen
circle.strokeColor = SKColor.whiteColor()
circle.fillColor = SKColor.orangeColor()
self.addChild(circle)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
circleRotate()
ballPosition()
// test ball position, this is the part with the issue I mentioned above.
println(balls.position) // (0, 0)
println(CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)) // (512.0,1408.0)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func circleRotate() {
let circleAction = SKAction.rotateByAngle(CGFloat(-M_PI * 2 / 3), duration: 0.1)
circle.runAction(SKAction.repeatAction(circleAction, count: 1))
}
func ballMove() {
let ballMovement = SKAction.moveTo(CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)), duration: 5)
balls.runAction(ballMovement)
}
func randomColor() {
let ballColorIndex = Int(arc4random_uniform(UInt32(ballColor.count)))
balls = SKShapeNode(circleOfRadius: 10 )
if ballColorIndex == 0 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.redColor()
// balls.zPosition = 10
ballMove()
} else if ballColorIndex == 1 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.blueColor()
// balls.zPosition = 10
ballMove()
} else if ballColorIndex == 2 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.greenColor()
// balls.zPosition = 10
ballMove()
} else if ballColorIndex == 3 {
balls.strokeColor = SKColor.whiteColor()
balls.fillColor = SKColor.yellowColor()
// balls.zPosition = 10
ballMove()
}
}
func ballPosition() {
let ballPointIndex = Int(arc4random_uniform(UInt32(points.count)))
if ballPointIndex == 0 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width)
randomColor()
self.addChild(balls)
} else if ballPointIndex == 1 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) - self.frame.size.height)
randomColor()
self.addChild(balls)
} else if ballPointIndex == 2 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame) - self.frame.size.width, y:CGRectGetMidY(self.frame))
randomColor()
self.addChild(balls)
} else if ballPointIndex == 3 {
balls.position = CGPoint(x:CGRectGetMidX(self.frame) + self.frame.size.width, y:CGRectGetMidY(self.frame))
randomColor()
self.addChild(balls)
}
}
}
The problem is you're overwriting ball in randomColor() - you're creating a new node which hasn't been added to the parent view.
The underlying problem is you've structured your code in a way that leads to confusion and mistakes. You should keep your functions single purposed. They should do what they say they do. A function called randomColor() should not move the move the ball or set the ball size. The position function should not set the color.
I rearranged the code and ran it. That bug is fixed. You can see what I changed and should be able to get further now. Good luck.
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var circle = SKShapeNode()
var ball = SKShapeNode(circleOfRadius: 10)
var ballColor = ["red", "blue", "green", "yellow"]
var points = ["up", "dowm", "left", "right"]
override func didMoveToView(view: SKView) {
backgroundColor = SKColor.whiteColor()
circle = SKShapeNode(circleOfRadius: 100 ) // Size of Circle
circle.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame)) //Middle of Screen
circle.strokeColor = SKColor.whiteColor()
circle.fillColor = SKColor.orangeColor()
self.addChild(circle)
self.addChild(ball)
ball.position = CGPointMake(150, 0)
println("Initial Ball Pos: \(ball.position)")
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
circleRotate()
setRandomBallPosition()
setRandomColor()
ballMove()
// test ball position
println("--------------")
println("Ball Pos: \(ball.position)")
println("Circle pos: \(circle.position)")
println("Midpoint: \(CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + self.frame.size.width))")
println("--------------")
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func circleRotate() {
let circleAction = SKAction.rotateByAngle(CGFloat(-M_PI * 2 / 3), duration: 0.1)
circle.runAction(SKAction.repeatAction(circleAction, count: 1))
}
func ballMove() {
let ballMovement = SKAction.moveTo(CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame)), duration: 5)
ball.runAction(ballMovement)
}
func setRandomColor() {
ball.strokeColor = SKColor.whiteColor()
let ballColorIndex = Int(arc4random_uniform(UInt32(ballColor.count)))
switch(ballColorIndex) {
case 0:
ball.fillColor = SKColor.redColor()
case 1:
ball.fillColor = SKColor.blueColor()
case 2:
ball.fillColor = SKColor.greenColor()
case 3:
ball.fillColor = SKColor.yellowColor()
default:
println("Unexpected random index value ", ballColorIndex)
}
}
func setRandomBallPosition() {
let ballPointIndex = Int(arc4random_uniform(UInt32(points.count)))
println("ballPointIndex = \(ballPointIndex)")
switch(ballPointIndex) {
case 0:
ball.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) + (self.frame.size.width / 2))
case 1:
ball.position = CGPoint(x:CGRectGetMidX(self.frame), y:CGRectGetMidY(self.frame) - (self.frame.size.height / 2))
case 2:
ball.position = CGPoint(x:CGRectGetMidX(self.frame) - (self.frame.size.width / 2), y:CGRectGetMidY(self.frame))
case 3:
ball.position = CGPoint(x:CGRectGetMidX(self.frame) + (self.frame.size.width / 2), y:CGRectGetMidY(self.frame))
default:
println("Unexpected random index value: ", ballPointIndex)
}
println("ball position = \(ball.position)")
}
}

Resources