SpriteKit Shadow falloff does not work - ios

So I have a simple light node and an object and I get this result.
and this is my code.
let light = SKLightNode()
light.categoryBitMask = 1
light.falloff = ... //I tried many values but nothing seems to change
addChild(light)
let block = SKSpriteNode(color: .black(), size: CGSize(90, 160))
block.zPosition = 2
block.shadowCastBitMask = 1
addChild(block)
I want to achieve this kind of look.
You can see the difference. Here the shadow gradually fades.
Am I doing something wrong? If not is there a 3rd party plugin that could work?
Thanks

Related

soft shadow, shadow blur in SceneKit

I add one node and try to setting shadow blur with SceneKit
here's my light config, I did try to set shadowRadius
light = [SCNLight light];
light.type = SCNLightTypeDirectional;
light.castsShadow = true;
light.shadowMode = SCNShadowModeForward;
light.shadowRadius = 5;
light.shadowMapSize=CGSizeMake(4000, 4000);
light.orthographicScale=25;
light.zNear=1;
light.zFar=1000;
but the result is not softer than when I not set shadowRadius
it' here:
I did try to add samplecount
light = [SCNLight light];
light.type = SCNLightTypeDirectional;
light.castsShadow = true;
light.shadowMode = SCNShadowModeForward;
light.shadowRadius = 5;
// add samplecount
light.shadowSampleCount = 5;
light.shadowMapSize=CGSizeMake(4000, 4000);
light.orthographicScale=25;
light.zNear=1;
light.zFar=1000;
result look like following
shadow seem soft but this shadow start from bottom of the node (z coordinate is 0). I spend a lot of time to set soft shadow only in the edge of node, not from bottom. But no result.
This problem also occurred when add two node cross over(not only node and geometry as SCNFloor)
My problem is how to get shadow blur(soft shadow) with direction light.
any help would be appreciated!
Swift 4 / Xcode 9.2
I got a pretty good result with these settings:
light2.castsShadow = true
light2.automaticallyAdjustsShadowProjection = true
light2.maximumShadowDistance = 20.0
light2.orthographicScale = 1
light2.shadowMapSize = CGSize(width: 2048, height: 2048)
light2.shadowMode = .forward
light2.shadowSampleCount = 128
light2.shadowRadius = 3
light2.shadowBias = 32
Increasing the shadowRadius to 12 helped a lot with my model, but then I needed to increase shadowSampleCount and shadowBias to not get artifacts.
I really can make shadow blur with orthographicScale. I don't know why, but this trick work for me. Hope can help someone
light.shadowMapSize=CGSizeMake(4000, 4000);
light.orthographicScale=100; // bigger is softer
I also change shadowMapSize to bigger value and setting isJitteringEnabled antialiasingMode to reduce aliasing.

SKEffectNode to an SKTexture?

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.

SpriteKit fast fullscreen vignette / lighting with alpha blending

I am making a platforming game with SpriteKit. I want to achieve the effect that only the area around the player is lit and everything else fades into darkness. Imagine something like a fake light source or a strong vignette. For testing purpose I created the following code:
let effect = SKEffectNode()
effect.zPosition = 100
effect.position = CGPoint(x: 0, y: 0)
effect.shouldRasterize = false
effect.blendMode = SKBlendMode.Multiply
let gray = SKSpriteNode()
gray.color = SKColor.grayColor()
gray.size = self.frame.size
gray.blendMode = SKBlendMode.Multiply
gray.position = CGPoint(x: 0, y: 0)
let shine = SKSpriteNode(imageNamed: "Shine")
shine.position = CGPoint(x: 0, y: 0)
shine.blendMode = SKBlendMode.Screen
shine.setScale(3.0)
effect.addChild(gray)
effect.addChild(shine)
self.world.addChild(effect)
This works as desired but has a somewhat significant impact on the frame rate. Considering that there might be additional effects like these from e. g. torches or other light sources on the map, I expect the frame rate to drop even more.
Next, I wanted to make the light flicker, so I assigned a random alpha value to the shine sprite in the update function. This really killed the frame rate, letting it drop instantly to about 4 frames.
I have read about SKEffectNodes and their heavy impact on the frame rate. Is there any other way to achieve this sort of fullscreen alpha blending, that is reasonably fast, even with multiple "lights"?
Any advice is truly appreciated.
Could this be solved with a SKLightNode perhaps?
https://developer.apple.com/library/prerelease/ios/documentation/SpriteKit/Reference/SKLightNode_Ref/index.html
Here's an article about how to use the SKLightNode:
http://www.ymc.ch/en/playing-with-ios-8-sprite-kit-and-sklightnode
Hope that's of any usage to you

SKAction.colorizeWithColor makes SKLabelNode disappear

I'm using SKLabelNode. I'm creating it and add it to my scene as a child and it displays with no problem, but when I try to change it's color (not fontColor) with the colorizeWithColor() method the label fades out.
Here is the line with the problem:
myLabel.runAction(SKAction.colorizeWithColor(SKColor.blueColor(), colorBlendFactor: 1.0, duration: duration))
I printed to the console the myLabel.color property after the completion of this action and here is what I get:
Optional(UIDeviceRGBColorSpace 0.99178 0.99178 1 0.00822043)
As you can see, the alpha value is almost 0, so I guess this is why the label disappears, but I don't understand why this is happening.
Thanks in advance.
UPDATE:
Ok, so I found actually this and is my bad that I didn't searched before asking. Here is the documentation about colorizeWithColor method:
This action can only be executed by an SKSpriteNode object. When the
action executes, the sprite’s color and colorBlendFactor properties
are animated to their new values.
So maybe anybody does know a good work around for colorize a SKLabelNode that is always updating?
!!!LAST UPDATE!!!
I was able to find a solution, but 0x141E came up with a even more nice solution, which I used and created the next method that works nice when you need to make transition from color A to color B. In solution suggested by 0x141E you ever come back to fontColor and it causes to blink when you change color. In my case it changes the fontColor, not the color property, which causes a pretty nice transition (Of course not great).
Thanks again 0x141E for the really nice approach!!!
Here is my solution:
This particular case works great when you call the method for parameter withDuration = 0.5
However if you need other time, you can play around with the sent withDuration parameter or the multiplier, that in my code is 5.
Even there of course should be a better solution so if you find please share it. For my needs this one works fantastic.
First of all a video so you can see how it works: https://www.youtube.com/watch?v=ZIz8Bn0-hUA&feature=youtu.be
func changeColorForLabelNode(labelNode: SKLabelNode, toColor: SKColor, withDuration: NSTimeInterval) {
labelNode.runAction(SKAction.customActionWithDuration(withDuration, actionBlock: {
node, elapsedTime in
let label = node as SKLabelNode
let toColorComponents = CGColorGetComponents(toColor.CGColor)
let fromColorComponents = CGColorGetComponents(label.fontColor.CGColor)
let finalRed = fromColorComponents[0] + (toColorComponents[0] - fromColorComponents[0])*CGFloat(elapsedTime / (CGFloat(withDuration)*5))
let finalGreen = fromColorComponents[1] + (toColorComponents[1] - fromColorComponents[1])*CGFloat(elapsedTime / (CGFloat(withDuration)*5))
let finalBlue = fromColorComponents[2] + (toColorComponents[2] - fromColorComponents[2])*CGFloat(elapsedTime / (CGFloat(withDuration)*5))
let finalAlpha = fromColorComponents[3] + (toColorComponents[3] - fromColorComponents[3])*CGFloat(elapsedTime / (CGFloat(withDuration)*5))
labelNode.fontColor = SKColor(red: finalRed, green: finalGreen, blue: finalBlue, alpha: finalAlpha)
}))
}
You can colorize an SKLabelNode with an SKAction by creating a custom action. Here's an example of how to do that
myLabel.color = SKColor.blueColor()
myLabel.colorBlendFactor = 0.0
let duration:NSTimeInterval = 2.0
myLabel.runAction(SKAction.customActionWithDuration(duration, actionBlock: {
node, elapsedTime in
let label = node as SKLabelNode
label.colorBlendFactor = elapsedTime / CGFloat(duration);
}))

SKLIghtNode with Swift

I have a player in the center of the screen, which I'd like to be the "source" of the light, so every other sprite, such as flying enemies, are casting a shadow.
I have this code:
light.categoryBitMask = 1
light.falloff = 1
light.shadowColor = UIColor.blackColor()
light.position = CGPoint(x: size.width / 2, y: size.height / 2)
addChild(light)
I'm totally new to Swift, never had any experience with SpriteKit, so I figured out that I should add this to every Sprite:
enemyOne.shadowCastBitMask = 1
What I get is quite interesting. The screen flickers everytime an enemy sprite is being spawned. But other than that, and a really small light in the center of the screen, where my player is, I'm not getting any shadows.
To make my plan more clear, I'd like to make it look like this:
This is what it looks like, as requested:

Resources