make vertical moving background - ios

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)
}
}

Related

What is the difference between SKScene.size.width SKSpriteNode.size.width?

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.

How to set ball's random position in Spritekit?

I am making a game where there are two balls on the scene, but the user is going to tap on the correct ball. However, when user taps on the correct ball, I want to set a random position for my second ball that when the user taps on the correct ball, I want to also want the second ball to move randomly.
Here is my code for setting up the random positions of the ball:
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
let shape = SKShapeNode()
currentBall.fillColor = pickColor()
//Rectangle
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160,height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
self.addChild(shape)
func randomBallPosition() -> CGPoint {
let xPosition = view!.scene!.frame.midX - viewMidX + CGFloat(arc4random_uniform(UInt32(viewMidX*2)))
let yPosition = view!.scene!.frame.midY - viewMidY + CGFloat(arc4random_uniform(UInt32(viewMidY*2)))
return CGPoint(x: xPosition, y: yPosition)
}
func handleTap() {
currentBall.position = randomBallPosition()
shape.position = randomBallPosition()
}
The issue is that you're setting the exact same position, which you have used for the first ball. As far as I understand both ball should appear at random independent positions. You should create a function to return a new position and call it for each ball:
class MyScene: SKScene {
var currentBall: SKShapeNode!
var shape: SKShapeNode!
override func didMove(to view: SKView) {
super.didMove(to: view)
let currentBall = SKShapeNode(circleOfRadius: CGFloat(size))
currentBall.fillColor = pickColor()
let shape = SKShapeNode()
shape.path = UIBezierPath(roundedRect: CGRect(x: 64, y: 64, width: 160,height: 160), cornerRadius: 50).cgPath
shape.fillColor = pickColor()
self.addChild(shape)
}
func handleTap() {
//your code here, except that you don't create new nodes, just modify existing ones
//at some point you will change balls' positions like this:
currentBall.position = randomBallPosition()
secondBall.position = randomBallPosition()
}
func randomBallPosition() -> CGPoint {
let xPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxX)! + 1)))
let yPosition = CGFloat(arc4random_uniform(UInt32((view?.bounds.maxY)! + 1)))
return CGPoint(x: xPosition, y: yPosition)
}
}
Hope this helps

Ball dropping animation not working (Swift)

This code is supposed to drop a ball from the top of the screen to the bottom. And once it touches the bottom of the screen, it should appear back to the top of the screen. It doesn't relocate to the top and it stops moving. I want it to be a continuous loop that resets the ball.y position every time it touches the bottom.
import SpriteKit
class GameScene: SKScene {
let ball = SKShapeNode(circleOfRadius: 20)
let label = SKLabelNode(fontNamed: "Futura")
let movingObjects = SKSpriteNode()
override func didMoveToView(view: SKView) {
let sceneBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
self.physicsBody = sceneBody
//Ball Transition
let ballTransition = SKAction.sequence([SKAction.fadeInWithDuration(1)])
ball.runAction(ballTransition)
//Ball function
ball.fillColor = SKColor.redColor()
ball.physicsBody = SKPhysicsBody(circleOfRadius: 25)
ball.physicsBody?.affectedByGravity = false
//Ball Movement
ball.position = CGPoint(x: self.frame.size.width/2, y: CGFloat(self.frame.size.height*1))
ballMovement()
movingObjects.addChild(ball)
self.addChild(label)
}
func ballMovement() {
let moveBall = SKAction.moveToY(self.frame.size.height*0, duration: 3)
let removeBall = SKAction.removeFromParent()
let moveAndRemove = SKAction.sequence([moveBall, removeBall])
ball.runAction(moveAndRemove)
//Label Sprite
label.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
label.fontColor = SKColor.redColor()
label.fontSize = 30
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
label.text = "\(ball.position.y)"
if ball.position.y < 26 {
ball.position = CGPoint(x: self.frame.size.width/2, y: CGFloat(self.frame.size.height*1))
}
}
}
removing the ball from its parent within the action and still acting on it elsewhere is going to become very fragile as your program gets more complex. Why not just do it all with the actions and let sprite kit worry about it ?
let moveBall = SKAction.moveToY(0, duration: 3)
let goBackUp = SKAction.moveToY(self.frame.size.height, duration:0)
let keepFalling = SKAction.sequence([moveBall, goBackUp])
ball.runAction(SKAction.repeatActionForever(keepFalling))

Right to Left Scrolling Background Swift / Sprite Kit

I have a scrolling background, but the background image appears to be "zoomed in" compared to the original image. The background scrolls just fine, but I'm not sure why the image is "zoomed in". Any help would be greatly appreciated.
class GameScene: SKScene, SKPhysicsContactDelegate {
var blueBall:SKSpriteNode!
var backgroundImage:SKSpriteNode!
var backgroundImage2:SKSpriteNode!
override func didMoveToView(view: SKView) {
self.view!.backgroundColor = UIColor(patternImage: UIImage(imageLiteral: "backgroundImage.png"))
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0)
self.physicsWorld.contactDelegate = self
blueBall = SKSpriteNode( imageNamed: "ball1111.png")
blueBall.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
blueBall.physicsBody = SKPhysicsBody(circleOfRadius: blueBall.size.width / 0.85)
blueBall.physicsBody!.dynamic = true
blueBall.physicsBody!.allowsRotation = true
self.addChild(blueBall)
blueBall.zPosition = 2
backgroundImage = SKSpriteNode(imageNamed: "backgroundImage.png")
self.addChild(backgroundImage)
backgroundImage.zPosition = 0
backgroundImage.anchorPoint = CGPoint(x: 0.5, y: 0.5)
backgroundImage.size.height = self.size.height
backgroundImage.size.width = self.size.width
backgroundImage.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
let backgroundTexture = SKTexture(imageNamed: "backgroundImage.png")
let shiftBackground = SKAction.moveByX(-backgroundTexture.size().width, y: 0, duration: 9)
let replaceBackground = SKAction.moveByX(backgroundTexture.size().width, y:0, duration: 0)
let movingAndReplacingBackground = SKAction.repeatActionForever(SKAction.sequence([shiftBackground,replaceBackground]))
for var i:CGFloat = 0; i<3; i++ {
let background = SKSpriteNode(texture: backgroundTexture)
background.position = CGPoint(x: backgroundTexture.size().width/2 + (backgroundTexture.size().width * i), y: CGRectGetMidY(self.frame))
background.size.height = self.frame.height
background.runAction(movingAndReplacingBackground)
self.addChild(background)
}
}
You have an issue with the image not being loaded correctly, so your numbers are off. Basically by loading SKSpriteNode(imageNamed: "backgroundImage.png") with the png extension, you are loading the actual file, not conforming to any retina rules. The width and height then would not be adjusted to handle this case. If you use an atlas inside the xcassets folder, it will allow you to specify the graphics for all display sizes, and will pick the correct one that is supported by the device.

Invisible circle stroke color in SKSpriteNode (iOS)

I'm trying to paint the stroke color in black, but it doesn't appear any line...
This is how it's displayed in the iOS simulator:
BUT, if I run the same code in the Xcode playground, the circle is perfect
My scene code:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.backgroundColor = UIColor.whiteColor()
let midcir = self.mainCircle()
self.addChild(midcir)
}
func mainCircle()->SKSpriteNode{
let node = SKSpriteNode()
node.anchorPoint=CGPoint(x: 0.5, y: 0.5)
let outsideNode = SKShapeNode(circleOfRadius: 127.5)
let insideNode = SKShapeNode(circleOfRadius: 1)
outsideNode.strokeColor = UIColor.blackColor()
outsideNode.fillColor = UIColor.blueColor()
outsideNode.lineWidth = 5
insideNode.strokeColor = UIColor.clearColor()
insideNode.fillColor = UIColor.clearColor()
node.addChild(outsideNode)
node.addChild(insideNode)
node.position = CGPoint(x: CGRectGetMidX(self.frame), y: CGRectGetMidY(self.frame))
return node
}
}
And the playground code, it's for OS X for live view incompatibilities (live view):
It only changes the UIColor for NSColor
import XCPlayground
import Cocoa
import SpriteKit
var myView = SKView(frame: NSRect(x: 0, y: 0, width: 400, height: 400))
var myScene = SKScene(size: CGSize(width: 400, height: 400))
myScene.backgroundColor = NSColor.whiteColor()
myView.presentScene(myScene)
func maincircle()->SKSpriteNode{
let node = SKSpriteNode()
node.anchorPoint=CGPoint(x: 0.5, y: 0.5)
let outsideNode = SKShapeNode(circleOfRadius: 127.5)
let insideNode = SKShapeNode(circleOfRadius: 1)
outsideNode.strokeColor = NSColor.blackColor()
outsideNode.fillColor = NSColor.blueColor()
outsideNode.lineWidth = 5
insideNode.strokeColor = NSColor.clearColor()
insideNode.fillColor = NSColor.clearColor()
node.addChild(insideNode)
node.addChild(outsideNode)
node.position = CGPoint(x: 200, y: 200)
return node
}
let node = maincircle()
myScene.addChild(node)
XCPShowView("MainCir", myView)
I don't know that is happening, I'll post the answer if I find it. Thank you!

Resources