Why is the layout of an skscene is not the same in all devices - sprite-kit

**hi
i want to make a brick shooter game and I started doing the border lines that are near the edges of the screen and I implemented the code(below) and it should work be the same on all devices but when I try it in an iPhone 8 everything goes fine and when I try it in an iPad or iPhonex it looks different like it zoomed in for some reason
Even the standard hello world label is the same
Can you please help me
And thanks **
func crwalls(){
// for i in 1...3 {
let spce = self.frame.width/12
let wall = SKSpriteNode(imageNamed: "liiine")
wall.size = CGSize(width: 20, height: self.frame.height/1.5)
wall.position = CGPoint(x: frame.minX + 30, y: frame.midY + (self.size.width/4) )
wall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: ((wall.size.width)), height: ((wall.size.height))))
//floor.physicsBody?.categoryBitMask = floorbit
wall.physicsBody?.isDynamic = false
print("mmm")
self.addChild(wall)
let wall3 = SKSpriteNode(imageNamed: "liiine")
wall3.size = CGSize(width: 20, height: self.frame.height/1.5)
wall3.position = CGPoint(x: frame.maxX - 30, y: frame.midY + (self.size.width/4) )
wall3.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: ((wall3.size.width)), height: ((wall3.size.height))))
//floor.physicsBody?.categoryBitMask = floorbit
wall3.physicsBody?.isDynamic = false
print("mmm")
self.addChild(wall3)
let wall4 = SKSpriteNode(imageNamed: "liiine")
wall4.size = CGSize(width: self.frame.width - 40, height: 20)
wall4.position = CGPoint(x: self.frame.midX, y: self.frame.maxY - spce)
wall4.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: ((wall4.size.width)), height: ((wall4.size.height))))
//floor.physicsBody?.categoryBitMask = floorbit
wall4.physicsBody?.isDynamic = false
print("mmm")
self.addChild(wall4)
}
‘’’

Yes I agree its annoying that Xcode doesn't have a 'do all option' to scale everything perfectly for all different device screen sizes.
Still here is something you might want to look into a little to see if these give you any better results:
SKSceneScaleMode.apple
So there are a few different options for scale mode that you can select. Have a play with the ones listed in the linked document. Just change the highlighted part in the GameViewController to what you would prefer
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
if let scene = SKScene(fileNamed: "MainScene") {
scene.scaleMode = .**aspectFill**
view.presentScene(scene)
}
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
override var shouldAutorotate: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
if UIDevice.current.userInterfaceIdiom == .phone {
return .allButUpsideDown
} else {
return .all
}
}
override var prefersStatusBarHidden: Bool {
return true
}
}

Simple answer to "Why is the layout of an skscene is not the same in all devices."
Because that would be impossible. This is like asking why can't clothing stores sell just 1 size clothing.
Screens come in all shapes and sizes, so it is up to you to determine how it will appear to the user.
The most simplest solution is building your game with a square scene. This will cover all aspect rations both tall and wide.
Of course, this may not be ideal to actual gameplay because you may end up losing more scene on the sides then you want.
Another solution is to make your SKView the reciprocal ratio of your scene and use .aspectFill on your view. This will make it so that your sides will show more information on wider devices, but your tall devices will receive the amount of information you want them to see.

Related

Why isn't Xcode showing my nodes overtime the simulator runs?

I have this code in my GameScene swift file:
var background = SKSpriteNode(imageNamed: "background")
let blockImage = SKTexture(imageNamed: "block")
let blockSize = CGSize(width: 64, height: 64)
override func didMove(to view: SKView) {
setUpGame()
}
func setUpGame(){
background.position = CGPoint(x: frame.size.width / 2, y: frame.size.height / 2)
addChild(background)
let block = SKSpriteNode(texture: blockImage, size: blockSize)
block.position.x = block.frame.width/2
block.position.y = frame.height - block.frame.height/2
addChild(block)
}
This is my code in my GameController file:
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
let scene = GameScene(size: self.view.bounds.size)
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFill
// Present the scene
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
// added to see physics body
view.showsPhysics = true
}
}
why does it take multiple runs for my block node to appear on the simulator except for my background? After the block finally show, it will continue to show after every run until I edit my code. Ive tried removing my background node just in case that might be the issue, but it didn't change anything

Render text with SKRenderer

I initialized SKRenderer in my project to use it alongside of metal. Although I'm not sure that I did it correctly, I need to know how I can render a simple 2D text with it?
For example, here is how we can draw a text with SKLabelNode, do we have anything like it for SKRenderer?
thank you so much
let winner = SKLabelNode(fontNamed: "Chalkduster")
winner.text = "You Win!"
winner.fontSize = 65
winner.fontColor = SKColor.red
winner.position = CGPoint(x: 100, y: 100)
view.addChild(winner)
The first thing you need to do is initialize the scene.
var renderer: SKRenderer
var skScene: SKScene
in your init function:
renderer = SKRenderer(device: device)
// iphone 11 portrait
skScene = SKScene(size: CGSize(width: 1125, height: 2436))
renderer.scene = skScene
let winner = SKLabelNode(fontNamed: "Chalkduster")
winner.text = "You Win!"
winner.fontSize = 65
winner.fontColor = SKColor.red
winner.position = CGPoint(x: 1125 / 2, y: 2436 / 2)
skScene.addChild(winner)
and finally in your render function:
renderer.render(withViewport: viewport, renderCommandEncoder: renderEncoder, renderPassDescriptor: renderPassDescriptor, commandQueue: commandQueue)
renderEncoder.endEncoding()
but keep in mind ui layer (SKRenderer) should be rendered in the last pass.

My SKTexture is size wrong for my SKSpriteNode

I have a 200 x 100 box-like menu button SKSpriteNode declared below.
menuBtn.size = CGSize(width: scrWidth * 1.5, height: 100)
menuBtn.texture = SKTexture(image: UIImage(named: "menuBtn")!)
The problem is, the hitbox for the button is still right, and the width of the SKTexture is fine, but the height of the SKTexture is around 1/2 the size of the actual menuBtn. It is a png, and I've checked and there's no clear textures around the sprite (just the transparent png ones), what am I doing wrong?
Pic of button: https://imgur.com/a/8BtXGLC
Pic of button in App: https://imgur.com/a/ZfW3xDL
Pic of how I want it to look: https://imgur.com/a/2zlSv8y
The texture's png image size is 1044x1044 if that has any impact.
First of all, make sure you are sending the correct size to your scene from GameViewController to your scene, in this example GameScene.
// without .sks
class GameViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
// ...
if let view = self.view as! SKView?
{
let scene = GameScene()
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFit
// Set anchorPoint and pass scene.size
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
scene.size = view.bounds.size
// Present the scene
view.presentScene(scene)
}
// ...
}
// with .sks
class GameViewController: UIViewController
{
override func viewDidLoad()
{
super.viewDidLoad()
// ...
if let view = self.view as! SKView?
{
if let scene = SKScene(fileNamed: "GameScene.sks")
{
// Set the scale mode to scale to fit the window
scene.scaleMode = .aspectFit
// Pass scene.size
scene.size = view.bounds.size
// Present the scene
view.presentScene(scene)
}
}
// ...
}
In your GameScene create the object. I recommend working in screenWidth and screenHeight when you create objects so they scale to all iOS devices.
class GameScene: SKScene
{
override func didMove(to view: SKView)
{
// ...
// scene width / height
let sceneWidth = size.width
let sceneHeight = size.height
// object
let menu : SKSpriteNode = SKSpriteNode()
menu.texture = SKTexture(imageNamed: "menu")
menu.size = CGSize(width: sceneWidth * 0.75, height: 100)
let y_pos : CGFloat = -sceneHeight * 0.5 + menu.size.height * 0.5
menu.position = CGPoint(x: 0, y: y_pos)
menu.zPosition = 1
addChild(menu)
// ...
}
}
The problem I had was that there were extra transparent pixels in my base image, all I had to do was remove them with GIMP.

how to scale the window to fit in all devices?

I want to know how can I scale the window to fit with the same size in all diveces. The problem is that in some diveces the objects doesnt cover the same space of how I want.
I have my scaleMode = .ResizeFill but the problem is that if I make it .AspectFill it doesnt appear in the correct place. I think that the problem is that I added a new container on the scene, but I dont know how to solve it.
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
if let scene = GameScene(fileNamed:"GameScene") {
// Configure the view.
let skView = self.view as! SKView
skView.showsFPS = true
skView.showsNodeCount = true
/* Sprite Kit applies additional optimizations to improve rendering performance */
skView.ignoresSiblingOrder = true
/* Set the scale mode to scale to fit the window */
scene.scaleMode = .ResizeFill
scene.position = view.center
skView.presentScene(scene)
}
}
override func didMoveToView(view: SKView) {
/* Setup your scene here */
scaleMode = .ResizeFill
node.position = view.center
// 3) Add the container to the scene
//addChild(node)
// 4) Set the sprite's x position
sprite.position = CGPointMake(radius, 0)
// 5) Add the sprite to the container
node.addChild(sprite)
// 6) Rotate the container
rotate()
sprite.color = UIColor.whiteColor()
sprite.colorBlendFactor = 1.0
sprite.zPosition = 4.0
circulo = SKSpriteNode(imageNamed: "circuloFondo2")
circulo.size = CGSize(width: 294, height: 294)
circulo.color = UIColor(red: 0.15, green: 0.15, blue: 0.15, alpha: 1)
circulo.colorBlendFactor = 1
circulo.alpha = 0.35
circulo.position = view.center
self.addChild(circulo)
circulo.zPosition = 1
}
The issue might be in code where you draw circle,You might be drawing
the circle with same radius for all screen sizes.
Instead of drawing the circle with the same radius, you need to
provide dynamic radius of the circle according to the device width.
Replace this
circulo.size = CGSize(width: 294, height: 294)
Use following snippet
let padding:CGFloat = 40.0
circulo.size = CGSize(width:view.frame.size.width - padding , height: view.frame.size.width - padding)

Swift SpriteKit Mario style rotating platform that stays horizontal

I am trying to create a mario style rotating platform that stays horizontal.
What I have done so far is create a simple class for this for testing purposes.
class PlatformRound: SKNode {
let platform: Platform
// MARK: - Init
init(barSize: CGSize, color: SKColor, pos: CGPoint) {
/// Base
let base = SKShapeNode(circleOfRadius: 6)
//base.name = Platform.Name.normal
base.fillColor = SKColor.darkGrayColor()
base.strokeColor = base.fillColor
base.position = pos
let rotatingAction = SKAction.rotateByAngle(CGFloat(-M_PI), duration: 8)
base.runAction(SKAction.repeatActionForever(rotatingAction))
/// Bar
let bar = Platform(size: barSize, color: color, pos: CGPointMake(0, 0 - (barSize.height / 2)), ofType: .Normal)
bar.zPosition = -200
/// Platform that supposed to stay horizontal
let platformSize = CGSizeMake(40, GameplayConfig.World.platformHeight)
let platformPos = CGPointMake(0, 0 - (bar.size.height / 2))
platform = Platform(size: platformSize, color: color, pos: platformPos, ofType: .Normal)
super.init()
addChild(base)
base.addChild(bar)
bar.addChild(platform)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am creating a roundBase that I can rotate. I than create a bar that goes down from the base, that is added to the base node. Finally I create the platform that is supposed to stay horizontal at all times.
I am using another Platform subclass to create the bar and platform, but they are not relevant to this question.
How can I make the platform stay horizontal. I tried the following which didnt work.
1) In update in my gameScene I constantly update the platform position or zRotation
platformRound.platform.zRotation = ...
2) Create a zRotation property that gets set once the platform is added and than use that property to constantly update the zRotation.
3) Tried playing around with physicsJoints
Im sure there is a easy way that I am missing. I would appreciate any help.
This should work:
class GameScene: SKScene{
override func didMoveToView(view: SKView) {
backgroundColor = .blackColor()
let centerSprite = SKSpriteNode(color: .whiteColor(), size: CGSize(width: 10, height: 10))
centerSprite.zPosition = 3
let platform = SKSpriteNode(color: .orangeColor(), size: CGSize(width: 70, height: 20))
platform.zPosition = 2
platform.name = "platform"
let container = SKNode()
container.position = CGPoint(x: frame.midX, y: frame.midY)
container.addChild(centerSprite) //Just for debugging
container.addChild(platform)
let radius = 120
let chain = SKSpriteNode(color: .grayColor(), size: CGSize(width: 3, height: radius))
chain.position = CGPoint(x: 0, y: radius/2)
container.addChild(chain)
platform.position = CGPoint(x: 0, y: radius)
let rotatingAction = SKAction.rotateByAngle(CGFloat(-M_PI), duration: 8)
container.runAction(SKAction.repeatActionForever(rotatingAction), withKey: "rotating")
addChild(container)
}
override func didEvaluateActions() {
self.enumerateChildNodesWithName("//platform") { node,stop in
if let parent = node.parent{
node.zRotation = -parent.zRotation
}
}
}
}
What I did, is that I have added platform node into container node and applied rotation to that container. Later on, in didEvaluateActions I've adjusted the rotation of platform (to have a negative value of its parent's zRotation). And that's it.
Here is the result of what I am seeing:
The adjusting is needed, because otherwise the platform will end-up rotating along with its parent (notice how white, center sprite is being rotated along with container node).