My UILabel in my SceneKit project will NOT update, and any help would be greatly appreciated! First I create the label
class GameViewController: UIViewController, SCNSceneRendererDelegate, UIGestureRecognizerDelegate, SCNPhysicsContactDelegate {
...
var highscore = Int()
var score = Int()
var scoreLabel = UILabel()
var highScoreLabel = UILabel()
...
Then I move into a function that creates the scene, which is called in the ViewDidLoad, and also in a function which is called later on when my character "dies."
func createScene(){
score = 0
...
scoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 100))
scoreLabel.textAlignment = .Center
scoreLabel.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 2 - self.view.frame.height)
scoreLabel.textColor = UIColor.grayColor()
self.view.addSubview(scoreLabel)
highScoreLabel = UILabel(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 100))
highScoreLabel.textAlignment = .Center
highScoreLabel.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 2 + self.view.frame.height)
highScoreLabel.textColor = UIColor.grayColor()
highScoreLabel.text = "Highscore : \(highscore)"
self.view.addSubview(highScoreLabel)
scoreLabel.alpha = 0
highScoreLabel.alpha = 0
UIView.animateWithDuration(0.5, delay: 1, usingSpringWithDamping: 0.4, initialSpringVelocity: 0.5, options: UIViewAnimationOptions.CurveLinear, animations: ({
() in
self.scoreLabel.alpha = 1
self.highScoreLabel.alpha = 1
self.highScoreLabel.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 2 + self.view.frame.height / 2.5)
self.scoreLabel.center = CGPoint(x: self.view.frame.width / 2, y: self.view.frame.height / 2 - self.view.frame.height / 2.5)
}), completion: nil)
This loads the scoreLabel and highScoreLabel onto the scene with an animation.
In the game, I have it so 2 SCNNodes collide, and this would call the addScore function...
func AddScore(){
score++
dispatch_async(dispatch_get_main_queue(), {
self.scoreLabel.text = "Score : \(self.score)"
})
print(score)
if score >= highscore{
highscore = score
let userDefault = NSUserDefaults.standardUserDefaults()
userDefault.setInteger(highscore, forKey: "highscore")
}
}
This function adds 1 to the score, and because I have the print statement, I can see that the score is going up, but my label will NOT update. I've put it into a dispatch_async like this...
override func viewDidLoad() {
super.viewDidLoad()
scene.physicsWorld.contactDelegate = self
self.createScene()
}
and that doesn't do anything. So, if I haven't made it clear, what am I doing wrong that my scoreLabel is not updating? Any help would be greatly appreciated! :)
Related
I'm new to swift 5.
I printed out the self.size.width in GameScene and the result is 677.0
I printed out the self.size.width from another class - lets say Ground and the result is 4002.0
I'm confused, please help.
Thanks a lot.
GameScene.swift:
import SpriteKit
class GameScene: SKScene {
let cam = SKCameraNode()
let bee = SKSpriteNode()
let ground = Ground()
override func didMove(to view: SKView) {
self.anchorPoint = .zero
//self.backgroundColor = UIColor(red: 0.4, green: 0.6, blue: 0.95, alpha: 1.0)
self.camera = cam
self.addingTheFlyingBee()
self.addBackground()
let bee2 = Bee()
bee2.position = CGPoint(x: 325, y: 325)
self.addChild(bee2)
let bee3 = Bee()
bee3.position = CGPoint(x: 200, y: 325)
self.addChild(bee3)
ground.position = CGPoint(x: -self.size.width * 2, y: 0)
ground.size = CGSize(width: self.size.width * 6, height: 0)
ground.createChildren()
self.addChild(ground)
}
override func didSimulatePhysics() {
self.camera!.position = bee.position
}
func addBackground() {
let bg = SKSpriteNode(imageNamed: "background-menu")
bg.position = CGPoint(x: 220, y: 220)
bg.zPosition = -1
self.addChild(bg)
}
func addingTheFlyingBee() {
bee.position = CGPoint(x: 250, y: 250)
bee.size = CGSize(width: 38, height: 34)
self.addChild(bee)
let beeAtlas = SKTextureAtlas(named: "Enemies")
let beeFrames : [SKTexture] = [
beeAtlas.textureNamed("bee"),
beeAtlas.textureNamed("bee-fly")
]
let flyAction = SKAction.animate(with: beeFrames, timePerFrame: 0.14)
let beeAction = SKAction.repeatForever(flyAction)
bee.run(beeAction)
let pathLeft = SKAction.moveBy(x: -200, y: -10, duration: 2)
let pathRight = SKAction.moveBy(x: 200, y: 10, duration: 2)
let flipTextureNegative = SKAction.scaleX(to: -1, duration: 0)
let flipTexturePositive = SKAction.scaleX(to: 1, duration: 0)
let flightOfTheBee = SKAction.sequence([ pathLeft, flipTextureNegative, pathRight, flipTexturePositive])
let neverEndingFlight = SKAction.repeatForever(flightOfTheBee)
bee.run(neverEndingFlight)
}
Ground.swift:
import Foundation
import SpriteKit
class Ground: SKSpriteNode, GameSprite {
var textureAtlas: SKTextureAtlas = SKTextureAtlas(named: "Environment")
var initialSize = CGSize.zero
func createChildren() {
self.anchorPoint = CGPoint(x: 0, y: 1)
let texture = textureAtlas.textureNamed("ground")
var tileCount: CGFloat = 0
let tileSize = CGSize(width: 35, height: 300)
while tileCount * tileSize.width < self.size.width {
let tileNode = SKSpriteNode(texture: texture)
tileNode.size = tileSize
tileNode.position.x = tileCount * tileSize.width
tileNode.anchorPoint = CGPoint(x: 0, y: 1)
self.addChild(tileNode)
tileCount += 1
}
}
func onTap() {}
}
I don't really understand the question. Since your scene width is 677.0px and your ground
width should be 6 * scene width so 4062px.
Is your question "Why is ground node width 4002px instead of 4062px?" or "What is the difference between those properties?"
If you question is the second one then this should be an answer:
SKScene.size.width will return width of the current scene while SKSpriteNode.size.width will return width of the target node in your case "ground" node.
self.size.width will return width of the current class you're editing.
So in GameScene.swift file if you call self.size.width inside a class GameScene: SKScene it will return GameScene width.
In Ground.swift file calling self.size.width inside a class Ground: SKSpriteNode, GameSprite will return Ground width.
I hope this answers your question.
I created a function that spawns a SKSpriteNode with name Ball and I created another function to animate it with 2 different colors. After they spawned I need to move one Ball to my touch.location, but when I create this it always takes the last spawned Node to move the location. So I just need to move one of a few balls that are currently in the scene and not the last one added.
Can someone please help me. Thanks in advance
// Spawn forever
let action = SKAction.sequence([SKAction.run(spawnRandomBall), SKAction.wait(forDuration: 1.2)])
run(SKAction.repeatForever(action))
}
//-------------------------------------------------------------------------------------------------//
func randomLocation (){
let randomNumber = Int(arc4random() % 2) // 0 and 1
if randomNumber == 0 {
roodball.physicsBody?.applyImpulse(CGVector(dx: 20, dy: 20))
blauwball.physicsBody?.applyImpulse(CGVector(dx: 20, dy: 20))
} else {
roodball.physicsBody?.applyImpulse(CGVector(dx: -20, dy: -20))
blauwball.physicsBody?.applyImpulse(CGVector(dx: -20, dy: -20))
}
}
//.....................................................................................................//
func spawnRandomBall (){
let randomNumber = Int(arc4random() % 2) // 0 and 1
if randomNumber == 0 {
spawnRoodBall()
} else {
spawnBlauwBall()
}
roodball.physicsBody = SKPhysicsBody(circleOfRadius: roodball.size.height / 2)
roodball.physicsBody?.allowsRotation = false
roodball.physicsBody?.affectedByGravity = false
roodball.physicsBody?.friction = 0
roodball.physicsBody?.restitution = 1
roodball.physicsBody?.linearDamping = 0
roodball.physicsBody?.angularDamping = 0
roodball.physicsBody?.velocity = CGVector(dx: 1, dy: 1)
blauwball.physicsBody = SKPhysicsBody(circleOfRadius: blauwball.size.height / 2)
blauwball.physicsBody?.allowsRotation = false
blauwball.physicsBody?.affectedByGravity = false
blauwball.physicsBody?.friction = 0
blauwball.physicsBody?.restitution = 1
blauwball.physicsBody?.linearDamping = 0
blauwball.physicsBody?.angularDamping = 0
blauwball.physicsBody?.velocity = CGVector(dx: 1, dy: 1)
randomLocation()
}
//.....................................................................................................//
func spawnRoodBall () {
roodball = SKSpriteNode(imageNamed: "BalRood")
roodball.position = CGPoint(x: 360, y: self.frame.midY - 38)
roodball.anchorPoint = CGPoint(x: 0.5, y: 0.5)
roodball.size = CGSize(width: 50, height: 50)
roodball.run(SKAction.animate(with: roodTextureArray, timePerFrame: 1.3))
roodball.zPosition = 40
let wait = SKAction.wait(forDuration: 6.5)
let remove = SKAction.removeFromParent()
roodball.run(SKAction.sequence([wait, remove]), withKey: "waitRemove")
self.addChild(roodball)
}
//...................................................................................................//
func spawnBlauwBall () {
blauwball = SKSpriteNode(imageNamed: "BalBlauw")
blauwball.position = CGPoint(x: 360, y: self.frame.midY - 38)
blauwball.anchorPoint = CGPoint(x: 0.5, y: 0.5)
blauwball.size = CGSize(width: 50, height: 50)
blauwball.run(SKAction.animate(with: blauwTextureArray, timePerFrame: 1.3))
blauwball.zPosition = 40
let wait = SKAction.wait(forDuration: 6.5)
let remove = SKAction.removeFromParent()
blauwball.run(SKAction.sequence([wait, remove]), withKey: "waitRemove")
self.addChild(blauwball)
}
I'm beginner in Spritekit and I try to make background move vertically
I did know how
this what in the controller
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scene = GameScene(size: view.bounds.size)
let skView = view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
skView.ignoresSiblingOrder = true
scene.scaleMode = .resizeFill
skView.presentScene(scene)
}
and this the scene
class GameScene: SKScene {
var background = SKSpriteNode()
override func didMove(to view: SKView) {
let backgroundTexture = SKTexture(imageNamed: "bbbgg.png")
let shiftBackground = SKAction.moveBy(x: 0, y: -backgroundTexture.size().height, duration: 5)
let replaceBackground = SKAction.moveBy(x: 0, y:backgroundTexture.size().height, duration: 0)
let movingAndReplacingBackground = SKAction.repeatForever(SKAction.sequence([shiftBackground,replaceBackground]))
for i in 1...3{
background=SKSpriteNode(texture:backgroundTexture)
background.position = CGPoint(x: 0, y: 0)
background.size.height = self.frame.height
background.run(movingAndReplacingBackground)
self.addChild(background)
}
}
}
the problem is the background move horizontally and after it arrives to the end of screen disappear, then begins move from the beginning of the screen again
I want it work vertically without disappearing
The background move horizontally because declarations of moveBy (shiftBackground, replaceBackground) works on the x axe (horizontally). You need to work on the y axe (vertically).
let shiftBackground = SKAction.moveBy(x: 0, y: -backgroundTexture.size().height, duration: 5)
let replaceBackground = SKAction.moveBy(x: 0, y: backgroundTexture.size().height, duration: 0)
At the first line, the background move from it position to it position - it height. At the second line, it return to it first position (I think moveBy is relative).
_
The background move again because you use the function SKAction.repeatForever (movingAndReplacingBackground). I think you can remove it.
let movingAndReplacingBackground = SKAction.sequence([shiftBackground,replaceBackground])
Edit :
I forget the loop.
In the loop, you give to the background an initial position.
background.position = CGPoint(x: backgroundTexture.size().width/2 + (backgroundTexture.size().width * CGFloat(i)), y: self.frame.midY)
Try to replace it by 0, middle if you want an immediately visible background
background.position = CGPoint(x: 0, y: self.frame.midY)
Or -height, 0 if you want the background appear by the top of the screen. In this case you need to replace shiftBackground and replaceBackground
background.position = CGPoint(x: -backgroundTexture.size().height, y: 0)
and
let shiftBackground = SKAction.moveBy(x: 0, y: backgroundTexture.size().height, duration: 5)
let replaceBackground = SKAction.moveBy(x: 0, y: 0, duration: 0)
I'm not sure, but I think, if you want only one mouvement, you can remove th sequence :
class GameScene: SKScene {
var background = SKSpriteNode()
override func didMove(to view: SKView) {
let backgroundTexture = SKTexture(imageNamed: "bbbgg.png")
let scrollByTopBackground = SKAction.moveBy(x: O, y: backgroundTexture.size().height, duration: 5)
background = SKSpriteNode(texture:backgroundTexture)
background.position = CGPoint(x: -backgroundTexture.size().height, y: self.frame.midY)
background.size.height = self.frame.height
background.run(scrollByTopBackground)
self.addChild(background)
}
}
Edit to answer you, try this...
class GameScene: SKScene {
var background = SKSpriteNode()
override func didMove(to view: SKView) {
let backgroundTexture = SKTexture(imageNamed: "bbbgg.png")
let shiftBackground = SKAction.moveBy(x: 0, y: -backgroundTexture.size().height, duration: 5)
let replaceBackground = SKAction.moveBy(x: 0, y:backgroundTexture.size().height, duration: 0)
let movingAndReplacingBackground = SKAction.repeatForever(SKAction.sequence([shiftBackground,replaceBackground]))
for i in 0...2 {
background=SKSpriteNode(texture:backgroundTexture)
background.position = CGPoint(x: 0, y: self.frame.midY + (backgroundTexture.size().height * CGFloat(i)))
background.size.height = self.frame.height
background.run(movingAndReplacingBackground)
self.addChild(background)
}
}
I have a very simple sample that allows to drag an UIView around. When touch up, I will have an inertia effect on the dragging direction for a second. While if I touch down again, I need to stop all the inertia animation and start to do another drag. Here is my code, the "clearAllAnimations" doesn't stop my animation. How could I implement that?
import UIKit
class ViewController: UIViewController {
var tile : UIView = UIView()
var labelView = UITextView()
var displayLink : CADisplayLink?
override func viewDidLoad() {
super.viewDidLoad()
tile.frame = CGRect(x: 0, y: 0, width: 256, height: 256)
tile.backgroundColor = UIColor.redColor()
view.addSubview(tile)
var panGesture = UIPanGestureRecognizer(target: self, action: Selector("panHandler:"))
view.addGestureRecognizer(panGesture)
labelView.frame = CGRect(x: 0, y: 100, width: view.frame.width, height: 44)
labelView.backgroundColor = UIColor.clearColor()
view.addSubview(labelView)
}
func panHandler (p: UIPanGestureRecognizer!) {
var translation = p.translationInView(view)
if (p.state == UIGestureRecognizerState.Began) {
self.tile.layer.removeAllAnimations()
}
else if (p.state == UIGestureRecognizerState.Changed) {
var offsetX = translation.x
var offsetY = translation.y
var newLeft = tile.frame.minX + offsetX
var newTop = tile.frame.minY + offsetY
self.tile.frame = CGRect(x: newLeft, y: newTop, width: self.tile.frame.width, height: self.tile.frame.height)
labelView.text = "x: \(newLeft); y: \(newTop)"
p.setTranslation(CGPoint.zeroPoint, inView: view)
}
else if (p.state == UIGestureRecognizerState.Ended) {
var inertia = p.velocityInView(view)
var offsetX = inertia.x * 0.2
var offsetY = inertia.y * 0.2
var newLeft = tile.frame.minX + offsetX
var newTop = tile.frame.minY + offsetY
UIView.animateWithDuration(1, delay: 0, options:UIViewAnimationOptions.CurveEaseOut, animations: {_ in
self.tile.frame = CGRect(x: newLeft, y: newTop, width: self.tile.frame.width, height: self.tile.frame.height)
}, completion: nil)
}
}
}
Setting UIViewAnimationOptions.AllowUserInteraction does the trick. The new code to start animation is this:
UIView.animateWithDuration(animationDuration, delay: 0, options:UIViewAnimationOptions.CurveEaseOut | UIViewAnimationOptions.AllowUserInteraction | UIViewAnimationOptions.BeginFromCurrentState, animations: {_ in
self.tile.frame = CGRect(x: newLeft, y: newTop, width: self.tile.frame.width, height: self.tile.frame.height)
}, completion: nil)
I want my background to be dynamic. But my hero running across the background is also dynamic. When I move him, he also affects my background.
Can 2 dynamic objects overlap each other without affecting each other's physics?
If so, how?
I tried giving them different categoryBitMasks but they still influence each other.
Still very new to iOS and programming in general.
Here's the code
override func didMoveToView(view: SKView) {
background.anchorPoint = CGPoint(x: 0, y: 0)
// background.color = UIColor(red: 0, green: 0, blue: 0, alpha: 1)
background.size = CGSize(width: size.width, height: size.height)
background.name = "bg"
background.position = CGPoint(x: 0, y: 0)
background.physicsBody = SKPhysicsBody(rectangleOfSize: background.size)
background.physicsBody?.dynamic = true
background.physicsBody?.categoryBitMask = bgCategory // 3
background.physicsBody?.contactTestBitMask = bgCategory
background.physicsBody?.collisionBitMask = noneCategory
addChild(background)
background2.anchorPoint = CGPoint(x: 0, y:0)
background2.size = CGSize(width: size.width, height: size.height)
background2.position = CGPoint(x: size.width, y: 0)
background2.physicsBody = SKPhysicsBody(rectangleOfSize: background2.size)
background2.physicsBody?.dynamic = true
background2.physicsBody?.categoryBitMask = bgCategory // 3
background2.physicsBody?.contactTestBitMask = bgCategory
background2.physicsBody?.collisionBitMask = noneCategory
addChild(background2)
scoreLabel.position = CGPoint(x: size.width * 0.5, y: size.height * 0.9)
scoreLabel.text = "\(score)"
addChild(scoreLabel)
physicsWorld.gravity = CGVectorMake(0, 0)
physicsWorld.contactDelegate = self
let boPhysicsSize = CGSizeMake(boWidth - boWidth/2, boHeight - boHeight/6)
bo.physicsBody = SKPhysicsBody(rectangleOfSize: boPhysicsSize)
bo.position = CGPoint(x: -(bo.size.width), y: size.height * 0.5)
bo.physicsBody?.dynamic = true
bo.physicsBody?.categoryBitMask = boCategory // 3
bo.physicsBody?.contactTestBitMask = chainsawCategory // 4
addChild(bo)
func rightImpulse () {
bo.physicsBody?.applyImpulse(CGVector(bo.size.width/40, 0))
background.physicsBody?.applyImpulse(CGVector(-(bo.size.width/40*100), 0))
background2.physicsBody?.applyImpulse(CGVector(-(bo.size.width/40*100), 0))
}
func leftImpulse () {
bo.physicsBody?.applyImpulse(CGVector(-(bo.size.width/40), 0))
background.physicsBody?.applyImpulse(CGVectorMake(bo.size.width/40*100, 0))
background2.physicsBody?.applyImpulse(CGVectorMake(bo.size.width/40*100, 0))
}
func upImpulse () {
bo.physicsBody?.applyImpulse(CGVector(0, bo.size.width/40))
}
func downImpulse () {
bo.physicsBody?.applyImpulse(CGVector(0, -(bo.size.width/40)))
}