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.
Related
I'm making a game and would like to create a universal scene size. Its height will be 2048, for example. The width of the level will be different depending on the level.
I want the height of the level (the play area) to fit completely on the screen of the device, regardless of the device whatever it is (iPhone, iPad).
GameViewController:
scene.scaleMode = .aspectFill
SKScene:
let cameraNode = SKCameraNode ()
let background = SKSpriteNode(imageNamed: "Background")
override func didMove(to view: SKView) {
background.size = CGSize(width: 2048, height: 1536)
background.position = CGPoint(x: 1024, y: 768)
addChild(background)
cameraNode.position = CGPoint(x: background.size.width / 2,
y: background.size.height / 2)
addChild(cameraNode)
camera = cameraNode
}
override func didChangeSize(_ oldSize: CGSize) {
let screenHeight = UIScreen.main.bounds.size.height
let backgroundHeight = background.size.height
let scaleFactor = backgroundHeight / screenHeight
cameraNode.setScale(scaleFactor)
}
different iOS devices will have dramatically different sizes. hence you need to scale your camera based on the size of the screen and the size of your background image.
GameViewController
scene.scaleMode = .resizeFill
SKScene
class GameScene: SKScene {
let cameraNode = SKCameraNode()
let background = SKSpriteNode(imageNamed: "background")
override func didMove(to view: SKView) {
//set up camera note
self.addChild(cameraNode)
self.camera = cameraNode
//add background image
self.addChild(background)
}
override func didChangeSize(_ oldSize: CGSize) {
//calculate scale factor based on screen size and image size
let screen_height = UIScreen.main.bounds.size.height
let background_height = background.size.height
let scale_factor = background_height/screen_height
cameraNode.setScale(scale_factor) //scale the camera accordingly
}
}
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
Somebody told me in one of my other questions that SpriteKit was easier than UI. I searched online on how to get started with SpriteKit, and I got this: https://www.raywenderlich.com/145318/spritekit-swift-3-tutorial-beginners. I put the images in and everything, I put this code in:
import SpriteKit
class GameScene: SKScene {
// 1
let player = SKSpriteNode(imageNamed: "player")
override func didMove(to view: SKView) {
// 2
backgroundColor = SKColor.white
// 3
player.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
// 4
addChild(player)
}
}
(the code they told me to put in), and when I run it, I just see a blank screen. On the tutorial, it had a ninja, but mine is just a blank screen.
Can anyone help with this?
if the screen is white try this :
import SpriteKit
class GameScene: SKScene {
// 1
var player = SKSpriteNode()
override func didMove(to view: SKView) {
// 2
backgroundColor = SKColor.white
// 3
let image = UIImage(named: "player")
let texture = SKTexture(image: image!)
player = SKSpriteNode(texture: texture)
player.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
// 4
addChild(player)
}
}
If the screen not white : make sure the scene presented correctly .
If you have the GameScene.sks :
In GameViewController :
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
let scene = SKScene(fileNamed: "GameScene")
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
If you don't have GameScene.sks File
In GameViewController :
override func viewDidLoad() {
super.viewDidLoad()
if let view = self.view as! SKView? {
// Load the SKScene from 'GameScene.sks'
let scene = GameScene(size : view.frame.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
}
}
I want to display a SKSpriteNode in front of a CAShapeLayer and this is the code that I am using :
import SpriteKit
import GameplayKit
import UIKit
class GameScene: SKScene {
let progressLayer = CAShapeLayer()
override func didMove(to view: SKView) {
let circle = UIView(frame: CGRect(x: 0, y: 0, width: self.size.width, height: self.size.height))
view.addSubview(circle)
view.sendSubview(toBack: circle)
progressLayer.frame = view.bounds
progressLayer.path = progressPath.cgPath
progressLayer.fillColor = UIColor.clear.cgColor
progressLayer.strokeColor = UIColor.green.cgColor
progressLayer.lineWidth = 20.0
let animation2 = CABasicAnimation(keyPath: "strokeEnd")
animation2.fromValue = 0.0
animation2.toValue = 1.0
animation2.duration = 1
animation2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseOut)
progressLayer.add(animation2, forKey: "drawLineAnimation")
circle.layer.addSublayer(progressLayer)
var popup = SKSpriteNode(imageNamed: "popupWorking.png")
popup.anchorPoint = CGPoint(x: 0.5, y: 0.5)
popup.position = CGPoint(x: self.size.width/2, y: self.size.height/2)
popup.size = CGSize(width: 400, height: 200)
popup.zPosition = 10
self.addChild(popup)
}
}
But when I run the code, the CAShapeLayer appears in front of the SKSpriteNode, how do I make the SKSpriteNode appear in front of the CAShapeLayer?
You'll need to change your view hierarchy at the view controller level. By default the view controller's view is an SKView. If you want to place a different UIView behind your SKView, you will need to adjust the view hierarchy.
Modify the view controller's view to be a UIView instead of an SKView. Then, add your shape layer's view as a subview to this main view. After that, add your SKView as a subview of the main view.
Your new view hierarchy should look like this:
View Controller
– view (UIView)
- circle view (UIView with your CAShapeLayer as a sublayer)
- spritekit view (SKView)
While it is possible to combine UIKit and SpriteKit in this way, it may be easier to stay within the SpriteKit world and recreate the circle animation using SKSpriteNodes instead of a CAShapeLayer.
Here is a playground that shows one way of doing it. I followed suggestion by #nathan .
Yellow line is drawn using SKShapeNode. The red shadow needs to be animated behind it and hence uses CAShapeLayer. (Note: I needed to mix the two as animating shadow path would be much more complicated in pure Sprite)
import SpriteKit
import PlaygroundSupport
private func invert(_ path: CGMutablePath) -> CGPath {
var rotation = CGAffineTransform(scaleX: 1.0, y: -1.0);
return path.copy(using: &rotation)!;
}
let bounds = CGRect(x: 0, y: 0, width: 400, height: 200)
let uiview = UIView(frame: bounds)
let pathview = UIView(frame: bounds)
let skview = SKView(frame: bounds)
PlaygroundPage.current.liveView = uiview
// Define the path
let path: CGMutablePath = CGMutablePath();
path.move(to: CGPoint(x:0, y:0))
path.addLine(to: CGPoint(x:200, y:100))
// Use CAShapeLayer to draw the red line
var pathLayer: CAShapeLayer = CAShapeLayer()
pathLayer.position = CGPoint(x: uiview.bounds.minX, y: uiview.bounds.minY + 200)
pathLayer.path = invert(path)
pathLayer.strokeColor = UIColor.red.cgColor
pathLayer.fillColor = nil
pathLayer.lineWidth = 10.0
pathLayer.lineJoin = kCALineJoinBevel
pathLayer.lineCap = kCALineCapRound
pathLayer.zPosition = 3;
let strokeAnimation = CABasicAnimation(keyPath: "strokeAnimation")
strokeAnimation.duration = 2;
strokeAnimation.fromValue = 0;
pathview.layer.addSublayer(pathLayer);
pathLayer.add(strokeAnimation, forKey: "strokePath");
uiview.addSubview(pathview)
//Use SKShapeNode to draw the yellow line
let pathShape: SKShapeNode = SKShapeNode(path: path);
pathShape.strokeColor = .white;
pathShape.lineWidth = 2.0;
pathShape.zPosition = 20;
// Create SK Scene
let scene = SKScene(size: CGSize(width: 400, height: 200))
scene.scaleMode = SKSceneScaleMode.aspectFill
scene.size = skview.bounds.size
scene.addChild(pathShape);
scene.backgroundColor = UIColor.clear;
skview.backgroundColor = UIColor.clear
skview.presentScene(scene);
uiview.insertSubview(skview, aboveSubview: pathview)
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)