So I'm trying to get a Node in xcode to appear, but it doesnt... here's the code.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let levelLabelNode = SKLabelNode(fontNamed: "Arial")
levelLabelNode.text = "Level"
levelLabelNode.fontSize = 30
levelLabelNode.fontColor = SKColor.green
levelLabelNode.position = CGPoint(x: self.frame.size.width/2, y: self.frame.size.height*0.75)
self.addChild(levelLabelNode)
}
}
This had me stuck for a while. The coordinate system of the programme (assuming you are running from the default) has point (0, 0) at the centre of the screen. self.frame.size.width refers to the entire width of the screen. Therefore, positioning a node at self.frame.size.width/2 would position a node at the rightmost edge of the screen. If you want to position your node centred horizontally and 3/4 up the screen try:
levelLabelNode.position = CGPoint(x: 0, y: self.frame.height/4)
Hope this helped!
Remember: The coordinate system starts with (0, 0) in the centre and self.frame.size.width is the entire width of the screen.
Related
All SKSpriteNodes have their origin, also called anchorPoint, if I'm not wrong, in their middle. I want to translate that origin to be in the middle of the top line of the node. I've tried the following inside the init() method, but it doesn't work:
self.anchorPoint = self.anchorPoint.applying(
CGAffineTransform.init(translationX: 0, y: self.frame.size.height/2)
)
PS: I'm using this to simplify the following:
I've created an array of SpriteNodes, which all have are in the new CGPoint.zero position, then, i'll have to apply this on them:
for i in 0..<arr.count {
arr[i].position.y += self.size.height*CGFloat((i+1)/(arr.count+1))
}
Here is the code:
import SpriteKit
class SKLayer: SKSpriteNode {
init(inside scene: SKScene, withLabels texts: [String]) {
super.init(texture: nil, color: .clear, size: scene.size)
self.anchorPoint = CGPoint(x: 0.5, y: 1)
converted(texts).forEach(self.addChild(_:))
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
self.position = .zero
}
private func converted(_ text: [String]) -> [SKNode] {
var arr = [SKLabelNode]()
text.forEach {
arr.append(SKLabelNode(text: $0))
}
for i in 0..<arr.count {
arr[i].position.y = CGFloat(i) * arr[0].frame.height
}
return arr
}
required init?(coder aDecoder: NSCoder) {
fatalError()
}
}
class GameScene: SKScene {
override func didMove(to view: SKView) {
self.addChild(
SKLayer(inside: self, withLabels: ["Welcome", "Hello", "Bye", "Help"])
)
}
}
Here's the pic of what the code compiles to:
I want the nodes to go from up to down, not the other way around. Iow, I wanna see the "Welcome" Label at the top, then the "texT" one, then the others.
According to the docs,
This is how anchor points work:
Anchor points are specified in the unit coordinate system, shown in the following illustration. The unit coordinate system places the origin at the bottom left corner of the frame and (1,1) at the top right corner of the frame. A sprite’s anchor point defaults to (0.5,0.5), which corresponds to the center of the frame.
From this image, we can see that to set the anchor point to the middle of the top border, you need to set it to (0.5, 1).
You tried to transform the anchor point to self.frame.size / 2 which is probably a number larger than 1. Well, according to the docs, the range of (0, 0) - (1, 1) covers the whole frame of the sprite node. If you set it to a value larger than (1, 1), the anchor point will be outside of the frame of the node.
I am trying to position a sprite at a point :
class GameScene: SKScene {
let player = SKSpriteNode(imageNamed: "Koopa_walk_1.png")
override func didMove(to view: SKView) {
player.position = CGPoint(x: 0, y: 0)
self.addChild(player)
print(koopa.get_x())
}
}
But for some reason my sprite appears in more or less the middle of the screen :
Edit :
This is the original image (260px by 320px) :
I expected to see the image appear in the top left because it's coordinates are (0, 0)
The coordinate system of SpriteKit is cartesian, with an origin default of the middle of the screen, when using the starting template in Apple's Xcode.
This template sets the origin to the centre of the screen by using an origin setting of (0.5, 0.5)
To have a top left origin, you're going to need set this to (0, 1), and then invert Y values, to negative values.
Explanation
I'm building a game in a iPhone 5s, but now I want to make it universal, so it can run in all iPhones (or at least 4s ahead) and all iPads (or at least iPad 2 ahead).
By now, I pretty much created those 3 images (1x, 2x and 3x). So there's a 50x50 square (#1x), a 100x100 square (#2x) and a 150x150 square (#3x).
This is the sample project (download here) I'm testing in:
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scene!.scaleMode = SKSceneScaleMode.ResizeFill //usually at GameViewController, not GameScene
let square = SKSpriteNode(imageNamed: "square")
square.anchorPoint = CGPointZero
square.position = CGPoint(x: self.frame.width / 1.095, y: self.frame.height / 1.1875) //superior right on iPhone 5/5s
addChild(square)
}
}
and these are the images:
Testing
When I run on each device, this is happens:
iPhone 4s - wrong position
iPhone 5/5s - right position (it was set up here)
iPhone 6/6s - wrong position
iPhone 6+/6s+ - wrong position
iPad 2 - wrong position
iPad Air/iPad Air 2 - wrong position
iPad Pro - wrong position
iPad Retina - wrong position
You can see better what happens by clicking on the image below.
Question
Basically, my question is: how can I make this universal? I mean, how can I make the square be positioned at the same relative place on the devices above?
Attempts
Michael Austin's attempt (download here)
import SpriteKit
// MARK: Screen Dimensions
let screenWidth = CGFloat(1024)
let screenHeight = CGFloat(768)
// MARK: Node Sizes
let square = SKSpriteNode(imageNamed: "square")
let nodeConstantWidth = screenWidth/square.size.width * 0.088
let nodeConstantHeight = screenHeight/square.size.height * 0.158
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scene!.scaleMode = SKSceneScaleMode.Fill //usually at GameViewController, not GameScene
square.xScale = nodeConstantWidth
square.yScale = nodeConstantHeight
square.anchorPoint = CGPointZero
square.position = CGPoint(x: screenWidth / 1.5, y: screenHeight / 1.5)
addChild(square)
}
}
Timmy Sorensen's attempt (download here)
import SpriteKit
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scene!.scaleMode = SKSceneScaleMode.ResizeFill //usually at GameViewController, not GameScene
let square = SKSpriteNode(imageNamed: "square")
square.anchorPoint = CGPointZero
square.position = CGPoint(x: self.frame.width / 1.095, y: self.frame.height / 1.1875) //superior right on iPhone 5/5s
addChild(square)
}
}
the square isn't positioning at the same place on every device, only on the device where position was set up. I made a little comparison below.
Try mathematically setting the size. Use UIScreen.mainScreen().bounds.width and UIScreen.mainScreen().bounds.height to find the device size. Use a little math to set up a proportion between your .sks scene and the device's size. Then use node.setScale to set the scale of your Sprite
Here is some sample code. Declare these as universal constants:
// MARK: Screen Dimensions
let screenSize = UIScreen.mainScreen().bounds
let screenWidth = screenSize.size.width
let screenHeight = screenSize.size.height
// MARK: Node Sizes
let node = SKSpriteNode(imageNamed: "image")
let nodeConstantWidth = screenWidth/node.size.width*0.3
If you were to move the declaration of nodeConstantWidth to GameScene, It will continually scale itself each time the scene is rendered. To avoid this, we just declare it once universally. Mathematically, the equation above will set the node's width to 30% of the screen's width on any device, and the height will be calculated accordingly. Change the '0.3' to any number to adjust this. Then in GameScene:
node.setScale(nodeConstantWidth)
This is how I placed my objects
scoreLbl.position = CGPoint(x: scene!.frame.width / 2, y: scene!.frame.height / 1.3)
airDefense.positon = CGPoint(x: frame.size.width / 2, y: frame.size.height / 5
This takes the screen and divides it out so its always in the same place no matter how big the screen is.
It is the same as using
CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
to place something in the middle of the screen.
I'm developing a small game using Swift & SpriteKit. When I add a SKSpriteNode for Restart button, it doesn't scale properly.
The size of Restart button is 100px in height and width. If I don't set scale , it covers the whole screen and make the screen appear white. I figured out that if I setScale to 0.005 only than it appears on screen, but not in proper size.
import Foundation
import SpriteKit
class EndScene: SKScene {
var restartBtn = SKSpriteNode()
override func didMoveToView(view: SKView) {
background()
restartGame()
}
func restartGame() {
restartBtn = SKSpriteNode(imageNamed: "restartBtn")
restartBtn.setScale(0.005)
restartBtn.position = CGPoint(x: self.size.width / 2, y: self.size.height / 4)
restartBtn.zPosition = 1
self.addChild(restartBtn)
}
func background() {
let bkg = SKSpriteNode(imageNamed: "Background")
bkg.size = self.frame.size
bkg.position = CGPoint(x: self.frame.width / 2, y: self.frame.height / 2)
bkg.zPosition = -2
self.addChild(bkg)
}
}
Here is the output of this code,
Restart Button Output
UPDATE
I put scene!.scaleMode = .AspectFill right inside didMoveToView function and it helped in rendering the shape of the SpriteNode properly. But still I have to setScale(0.001) to make the size of Restart button fit in the screen. Can anyone assist me what line of code I'm still missing?
Instead of using .setScale try using restartBtn.size = CGSize(Width: 50, Height: 50)
This uses the Sprite Kit's resize formula.
I ran into this as well. It happened when I forgot to specify the size of the scene. Instead of calling initWithSize I just wrote init and the scene started to scale down all nodes by x1000. Nodes were visible, but they required a scale of 0.001
I'm making a game and I'd like to have my background scroll vertically endlessly but I have no clue how to start. I'm pretty new to coding and sprite kit especially so any help would be greatly appreciated.
Anyone know how I could go about doing this?
You want to have two nodes, one to start with and one that follows it. Lastly, loop forever.
func createGroundForward() {
let groundTexture = SKTexture(imageNamed: "Track")
for i in 0 ... 1 {
let ground = SKSpriteNode(texture: groundTexture)
ground.zPosition = -4
ground.position = CGPoint(x: 0, y: -groundTexture.size().height + (groundTexture.size().height + (groundTexture.size().height * CGFloat(i))))
addChild(ground)
let moveLeft = SKAction.moveBy(x:0 , y: -groundTexture.size().height, duration: 5)
let moveReset = SKAction.moveBy(x:0 , y: groundTexture.size().height, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
ground.run(moveForever)
}
}
Then add the function to your didMove function (which is like viewDidLoad for Sprite).
override func didMove(to view: SKView) {
self.anchorPoint = CGPoint(x: 0.5, y: 0.5)
createGroundForward()
}
I will give you an answer conceptually to give you a place to start since you have not provided any code.
You will need multiple images that are the size of the screen and stack them on top of each other vertically.
You then want to move these images down point by point. When the top of one of the images reaches the bottom of the screen, put it back on top of your stack of images. This will cause a never ending scrolling of your background.
Best of luck!