I'm in a trouble of SpriteKit's 'addChild' method.
I tried to add a simple button to my scene , and the button is formed of one background image and one label.So I wrote code like this:
override func didMove(to view: SKView) {
let buttonBg = SKSpriteNode(imageNamed: "mainButton_green")
let buttonLabel = SKLabelNode(text: "Label")
self.addChild(buttonBg)
self.addChild(buttonLabel)
}
Then I ran the program on simulator and I found that result is strange.Sometimes it appeared right,like this:
correct appearence
But sometimes, 'buttonLabel' will be behind of 'buttonBg', just like this:
wrong appearence
Why?
ps. I had print self.children , and the result was '[buttonBg,buttonLabel]' in each situation , my skview.ignoresSiblingOrder was always false.
A couple of things you can do
let buttonBg = SKSpriteNode(imageNamed: "mainButton_green")
buttonBg.zPosition = 1
let buttonLabel = SKLabelNode(text: "Label")
buttonLabel.zPosition = 2
which manually sets the z layering of your objects
I also think that if you are creating a button object you should add the label as a child of the background
let buttonBg = SKSpriteNode(imageNamed: "mainButton_green")
buttonBg.zPosition = 1
let buttonLabel = SKLabelNode(text: "Label")
buttonLabel.zPosition = 1
self.addChild(buttonBg)
buttonBg.addChild(buttonLabel)
I always add a zPosition even if its the only child.
Your issue is not a real problem: when you create your elements, following your case they'll be rendered in the main thread following some rules like:
statements order
zPosition property
alpha property
etc..
So, something like a litlle label could be drawed before your button background causing this annoying aspect.
A good thing to do when you want to create a button should be:
let buttonBg = SKSpriteNode(imageNamed: "mainButton_green")
self.addChild(buttonBg)
let buttonLabel = SKLabelNode(text: "Label")
buttonBg.addChild(buttonLabel)
In other words, put your label as a child of your background, this should will guarantee to you a correct zPosition levels (without change zPosition properties), a correct label position (by default CGPoint.zero) always if you haven't changed (leave untouched) the anchorPoint of your elements.
Related
I would like to define a clickable zone in a picture for an iOS Project.
It seems like "Where's Wally". When I clicked in a specific zone, Swift do this action.
There are the swipe to manage too.
I thought to make a transparent button but I don't know if it's possible..
Have you any ideas ?
Thank's ! :-)
For iOS, you can do it this way:
(1) Remember to set your UIImageView to receive user interactions, and add a UITapGestureRecognizer to the UIImageView.
myImageView.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer()
tapGesture.numberOfTapsRequired = 1
tapGesture.addTarget(self, action: #selector(tapDetected())
(2) Create a new CALayer, size it, position it, and add it to the UIImageView's layer.
let tappableLayer = CALayer() // place this as a global variable
tappableLayer.frame = // give a CGRect size here, or you can make it a shape by declaring a UIBezierPath and making it a CAShapeLayer
myImageView.layer.addSublayer(tappableLayer)
(3) Code the tapDetected function.
func tapDetected(_ recognizer:UITapGestureRecognizer) {
let p = recognizer.location(in: self)
if tappableLayer.hitTest(p) != nil {
// user tapped in your defined area of the image
}
}
In SpriteKit you are able to enumerate through each node with a specific name. Say I have 10 sprites with the name property set to "foo" I can then run the code below and it will move each "food" node up 5 pixels every time the function is called.
enumerateChildNodesWithName("foo"){node, stop in
let sprite:SKSpriteNode = node as! SKSpriteNode
sprite.position.x += 5
}
Now, I would like to do this with UIImageView (if possible).
Here's my current setup
In my app I have code that runs every second. It is supposed to add a UIImageView using the following code
var mView:UIImageView = UIImageView(image: UIImage(named: "swirl"))
self.view.addSubview(mView)
Finally, I have a for loop that doesn't seem to be targeting each specific view. But it should be moving each individual image view in a circle. Currently it only looks like a single image is moving even while I think I've added more image views.
for view in self.view.subviews as [UIView] {
if let ind = view as? UIImageView {
let OrCe:CGPoint = mView.center
mView.center = CGPoint(x: OrCe.x + sin(tick)*50,
y: OrCe.y + cos(tick)*50)
}
}
I feel like what I'm doing is really wrong :( Is it possible for me to do what I am trying to do? I would like to do this so that I do not have to use SpriteKit. I want to try and create graphics at a lower level framework. Can I go lower then this even? How can I most efficiently render these 10 moving images?
I assume all the two peaces are consecutive.
var mView:UIImageView = UIImageView(image: UIImage(named: "swirl"))
self.view.addSubview(mView)
for view in self.view.subviews as [UIView] {
if let ind = view as? UIImageView {
let OrCe:CGPoint = view.center
view.center = CGPoint(x: OrCe.x + sin(tick)*50,
y: OrCe.y + cos(tick)*50)
}
}
If got you correct this should work.
I'm just trying to make a SKLabelNode fade in, here's my code:
let welcome = SKLabelNode(text: "Welcome")
welcome.fontName = "HelveticaNeue-Light"
welcome.fontSize *= size.width/welcome.frame.width
welcome.fontColor = UIColor(white:1,alpha:0)
welcome.horizontalAlignmentMode = .center
welcome.verticalAlignmentMode = .center
welcome.position = CGPoint(x:size.width/2,y:size.height/2)
addChild(welcome)
let fadein = SKAction.fadeIn(withDuration: 1)
let remove = SKAction.removeFromParent()
welcome.run(SKAction.sequence([fadein,remove]))
But it doesn't work, and I can't figure out why. The strange part is the removeFromParent part works fine, just not the fade in. I already tried changing the font, making the label fade out and even making a custom action that changes the alpha, all of which have failed. I just can't figure out what the problem is.
Any idea will be appreciated.
Thanks in advance.
Instead of setting the fontColor's alpha to 0, set the SKLabelNode's alpha to 0 before running the fadeIn action on it. This is because the actions are applied to the nodes themselves, not to property inside of the nodes. (E.G. In your case: fadeIn affects SKLabel.alpha, not SKLabel.fontColor.alpha)
SKEffectionNodes have a shouldRasterise "switch" that bakes them into a bitmap, and doesn't update them until such time as the underlying nodes that are impacted by the effect are changed.
However I can't find a way to create an SKTexture from this rasterised "image".
Is it possible to get a SKTexture from a SKEffectNode?
I think you could try a code like this (it's just an example):
if let effect = SKEffectNode.init(fileNamed: "myeffect") {
effect.shouldRasterize = true
self.addChild(effect)
...
let texture = SKView().texture(from: self)
}
Update:
After you answer, hope I understood better what do you want to achieve.
This is my point of view: if you want to make a shadow of a texture, you could simply create an SKSpriteNode with this texture:
let shadow = SKSpriteNode.init(texture: <yourTexture>)
shadow.blendMode = SKBlendMode.alpha
shadow.colorBlendFactor = 1
shadow.color = SKColor.black
shadow.alpha = 0.25
What I want to say is that you could proceed step by step:
get your texture
elaborate your texture (add filters, make some other effect..)
get shadow
This way of working produces a series of useful methods you could use in your project to build other kind of elements.
Maybe, by separating the tasks you don't need to use texture(from:)
I've figured this out, in a way that solves my problems, using a Factory.
Read more on how to make a factory, from BenMobile's patient and clear articulation, here: Factory creation and use for making Sprites and Shapes
There's an issue with blurring a SKTexture or SKSpriteNode in that it's going to run out of space. The blur/glow goes beyond the edges of the sprite. To solve this, in the below, you'll see I've created a "framer" object. This is simply an empty SKSpriteNode that's double the size of the texture to be blurred. The texture to be blurred is added as a child, to this "framer" object.
It works, regardless of how hacky this is ;)
Inside a static factory class file:
import SpriteKit
class Factory {
private static let view:SKView = SKView() // the magic. This is the rendering space
static func makeShadow(from source: SKTexture, rgb: SKColor, a: CGFloat) -> SKSpriteNode {
let shadowNode = SKSpriteNode(texture: source)
shadowNode.colorBlendFactor = 0.5 // near 1 makes following line more effective
shadowNode.color = SKColor.gray // makes for a darker shadow. White for "glow" shadow
let textureSize = source.size()
let doubleTextureSize = CGSize(width: textureSize.width * 2, height: textureSize.height * 2)
let framer = SKSpriteNode(color: UIColor.clear, size: doubleTextureSize)
framer.addChild(shadowNode)
let blurAmount = 10
let filter = CIFilter(name: "CIGaussianBlur")
filter?.setValue(blurAmount, forKey: kCIInputRadiusKey)
let fxNode = SKEffectNode()
fxNode.filter = filter
fxNode.blendMode = .alpha
fxNode.addChild(framer)
fxNode.shouldRasterize = true
let tex = view.texture(from: fxNode) // ‘view’ refers to the magic first line
let shadow = SKSpriteNode(texture: tex) //WHOOPEE!!! TEXTURE!!!
shadow.colorBlendFactor = 0.5
shadow.color = rgb
shadow.alpha = a
shadow.zPosition = -1
return shadow
}
}
Inside anywhere you can access the Sprite you want to make a shadow or glow texture for:
shadowSprite = Factory.makeShadow(from: button, rgb: myColor, a: 0.33)
shadowSprite.position = CGPoint(x: self.frame.midX, y: self.frame.midY - 5)
addChild(shadowSprite)
-
button is a texture of the button to be given a shadow. a: is an alpha setting (actually transparency level, 0.0 to 1.0, where 1.0 is fully opaque) the lower this is the lighter the shadow will be.
The positioning serves to drop the shadow slightly below the button so it looks like light is coming from the top, casting shadows down and onto the background.
I am creating a game and I set up the background already. Now when I put in the code to add the character to the scene it does not appear. This is the code I used to try and get the character to appear on the screen. Apparently my character shows up behind the background. Is there another way to get the character to show in front of the background?
func createPlayer() -> SKNode {
let playerNode = SKNode()
playerNode.position = CGPoint(x: self.size.width / 2, y: 80.0)
let sprite = SKSpriteNode(imageNamed: "Pig")
playerNode.addChild(sprite) return playerNode }
In order to make the character appear in front of the background image you need to use zposition:
//You need to choose a high zposition number to make sure it shows up in front.
playerNode.zPosition = 1
// you should set the background images z position using the same method to something below 1
I would also recommend using this to add your character to the scene:
self.addChild(playerNode)