Right to Left Scrolling Background Swift / Sprite Kit - ios

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.

Related

SKSpriteNode shadow rendering incorrectly?

I'm using Xcode 11.4.1 and Swift 5.2.2, and I am trying cast a shadow using an SKLightNode onto a circular SKSpriteNode.
However, the shadow is cast over the rectangular frame of the SpriteNode rather than the circle image png that I am using.
This is the desired effect
This is what happens
In the first image, I am using a 611x611px circle png while in the second I am using a 612x612 png. I have found that this "corner shadow" happens only when using specific image dimensions to create the SpriteNode.
Specifically, through my testing, square images with size <688px and >601px display no corner shadow, except sizes exactly 612 and 608. I am testing this in a playground but the same problem occurs in a full xcodeproj.
What have I done wrong here? I doubt my code is the issue but here it is:
import PlaygroundSupport
import SpriteKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode(color: UIColor.blue, size: frame.size)
background.zPosition = 0
background.position = CGPoint(x: frame.midX, y: frame.midY)
background.lightingBitMask = 1
addChild(background)
let light = SKLightNode()
light.zPosition = 100
light.categoryBitMask = 1
light.falloff = 0.5
light.lightColor = UIColor.white
light.position = CGPoint(x: frame.midX, y: frame.midY)
addChild(light)
let sprite = SKSpriteNode(imageNamed: "612.png")
sprite.color = UIColor.yellow
sprite.colorBlendFactor = 1
sprite.zPosition = 3
sprite.position = CGPoint(x: frame.midX, y: frame.midY + 100)
sprite.size = CGSize(width: 100, height: 100)
sprite.physicsBody = SKPhysicsBody(circleOfRadius: 50)
sprite.physicsBody?.categoryBitMask = 2
sprite.shadowCastBitMask = 1
sprite.lightingBitMask = 1
sprite.physicsBody?.isDynamic = false
addChild(sprite)
}
}
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480))
let scene = GameScene()
scene.scaleMode = .aspectFill
scene.size = sceneView.frame.size
sceneView.presentScene(scene)
PlaygroundSupport.PlaygroundPage.current.liveView = sceneView

SpriteKit: A gap between SKSpriteNodes

When the blue square falls down on the red one a gap will remain. But when I make the blue square fall from a lower position there is no gap. Also when I give the blue square a higher mass the gap (on the ground) under the red square disappears and when the mass is too high the red square is gone.
I also made the squares smaller because the amount of pixels didn't equal the pixels of the png file which led to an even bigger gap. And when I set "showsPhysics" to true there is also a gap to see and even bigger when the squares are not scaled down.
Gap between SKSpriteNodes (picture)
//: Playground - noun: a place where people can play
import UIKit
import SpriteKit
import PlaygroundSupport
class Scene: SKScene {
var square = SKSpriteNode(texture: SKTexture(imageNamed: "red"), size: SKTexture(imageNamed: "red").size())
var square2 = SKSpriteNode(texture: SKTexture(imageNamed: "blue"), size: SKTexture(imageNamed: "blue").size())
var label = SKLabelNode(fontNamed: "Chalkduster")
override func didMove(to view: SKView) {
self.addChild(square)
self.addChild(square2)
self.addChild(label)
}
override func didChangeSize(_ oldSize: CGSize) {
// Add Scene Physics
self.physicsBody?.usesPreciseCollisionDetection = true
self.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: -1, y: -1, width: self.size.width + 2, height: self.size.height + 2))
self.backgroundColor = UIColor.white
self.physicsBody?.usesPreciseCollisionDetection = true
//Add square
square.texture?.usesMipmaps = true
square.zPosition = 11
square.physicsBody = SKPhysicsBody(rectangleOf: square.size)
square.physicsBody?.usesPreciseCollisionDetection = true
square.physicsBody?.mass = 0.1
square.physicsBody?.allowsRotation = false
square.physicsBody?.usesPreciseCollisionDetection = true
square.setScale(0.25)
square.position = CGPoint(x: self.frame.width / 2, y: self.square.size.height)
square.name = "square"
//Add square2
square2.texture?.usesMipmaps = true
square2.zPosition = 11
square2.physicsBody = SKPhysicsBody(rectangleOf: square2.size)
square2.physicsBody?.usesPreciseCollisionDetection = true
square2.physicsBody?.mass = 0.1
square2.physicsBody?.allowsRotation = false
square2.physicsBody?.usesPreciseCollisionDetection = true
square2.setScale(0.25)
square2.position = CGPoint(x: self.frame.width / 2, y: self.square.size.height * 6)
square2.name = "square2"
//Add label
label.fontSize = 30
label.text = "w: \(self.frame.size.width) h: \(self.frame.size.height)"
label.position = CGPoint(x: self.frame.size.width / 2, y: self.frame.size.height - 30)
label.fontColor = UIColor.black
label.zPosition = 100
}
}
class ViewController: UIViewController {
let scene = Scene()
var viewSize = CGSize(width: 0, height: 0)
override func loadView() {
self.view = SKView()
}
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
view.frame = CGRect(origin: CGPoint(x: -1, y: -1), size: viewSize)
view.presentScene(scene)
view.showsPhysics = false
view.showsFPS = true
view.showsNodeCount = true
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
scene.size = CGSize(width: self.view.frame.size.width, height: self.view.frame.size.height)
}
}
PlaygroundPage.current.liveView = ViewController()
As proof from the print commands, there is no actual gap between the sprite nodes, and with second proof from a super-zoomed-in-screenshot:
The playground you provided does not replicate an issue, which makes me wonder... what is going on with your project?
pg #2:
The scene's width and height has to be multiplied by 2. So the scene has to be 4 times bigger than the ViewControllers view. It's also like that in the game template that Apple provides for Spritekit.
scene.size = CGSize(width: self.view.frame.size.width * 2, height: self.view.frame.size.height * 2)
https://github.com/simon2204/Playground

make vertical moving background

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

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

How can I apply friction to a circular SKPhysicsBody

I am trying to understand friction, or some other aspect of physics, in SpriteKit. I created a circle that rotates. I placed a square on top of the rotating circle. I expected the square to rotate along with the circle and fall off, but the square does not move. I set physicsBody.friction = 1.0 in an attempt to make the surfaces sticky but it does not help. What changes to physics do I need to make so that the square is affected by the circle's rotation?
I created a self-contained example using a Playground. To test:
Download attached bluecircle.png and place it on your desktop (or wherever)
Copy code below into your Playground
Change NSImage path to wherever you saved bluecircle.png
Code:
import Cocoa
import SpriteKit
import XCPlayground
let screenWidth = 1400.0
let screenHeight = 1000.0
let xOffset = 300.0
let smallsquareSize = CGSize(width: 100, height: 100)
let bluesquareSize = CGSize(width: 400 , height: 400)
// the img is a blue circle w/ radius of 200 located on my desktop
let img = NSImage(byReferencingFile: "/Users/CHANGETHIS/Desktop/bluecircle.png")
class PlayScene: SKScene {
let redsquare = SKSpriteNode(color: SKColor.redColor(), size: smallsquareSize)
let greensquare = SKSpriteNode(color: SKColor.greenColor(), size: smallsquareSize)
let bluesquare = SKSpriteNode(color: SKColor.blueColor(), size: bluesquareSize)
let bluecircle = SKSpriteNode(texture: SKTexture(image: img))
var lastTime = NSTimeInterval(0)
init(size: CGSize) {
super.init(size: size)
}
override func didMoveToView(view: SKView!) {
backgroundColor = SKColor.blackColor()
// RED SQUARE
redsquare.position = CGPoint(x: screenWidth/2 - xOffset, y: screenHeight - 100)
redsquare.physicsBody = SKPhysicsBody(rectangleOfSize: smallsquareSize)
redsquare.physicsBody.friction = 1.0
addChild(redsquare)
// BLUE SQUARE
bluesquare.position = CGPoint(x: screenWidth / 2 - xOffset, y: screenHeight / 2)
bluesquare.physicsBody = SKPhysicsBody(rectangleOfSize: bluesquareSize)
bluesquare.physicsBody.dynamic = false
bluesquare.physicsBody.affectedByGravity = false
bluesquare.physicsBody.friction = 1.0
addChild(bluesquare)
// GREEN SQUARE
greensquare.position = CGPoint(x: screenWidth/2 + xOffset, y: screenHeight - 100)
greensquare.physicsBody = SKPhysicsBody(rectangleOfSize: smallsquareSize)
greensquare.physicsBody.friction = 1.0
addChild(greensquare)
// BLUE CIRCLE
bluecircle.position = CGPoint(x: screenWidth / 2 + xOffset, y: screenHeight / 2)
bluecircle.physicsBody = SKPhysicsBody(circleOfRadius: 200)
bluecircle.physicsBody.dynamic = false
bluecircle.physicsBody.affectedByGravity = false
bluecircle.physicsBody.friction = 1.0
addChild(bluecircle)
}
override func update(currentTime: NSTimeInterval) {
let delta = currentTime - lastTime
lastTime = currentTime
bluesquare.zRotation += delta
bluecircle.zRotation += delta
}
}
var view = SKView(frame:NSRect(x: 0.0, y: 0.0, width: screenWidth, height: screenHeight))
var playScene = PlayScene(size: CGSize(width: screenWidth, height: screenHeight))
view.presentScene(playScene)
view.showsFPS = true
view.showsDrawCount = true
view.showsNodeCount = true
XCPShowView("View", view)
`
I think the answer to your question are Joints . Creating a fixed joint between your nodes I believe will solve your problem .
https://developer.apple.com/library/ios/documentation/SpriteKit/Reference/SKPhysicsJoint_Ref/Reference/Reference.html

Resources