SKLightNode cast shadow issue
Asked Answered
L

2

7

I have been unsuccessful in getting a SKSpriteNode to cast a shadow AND also be made to disappear when going into a shadow from the same light source. I am able to do one of the two but not both.

According to the docs: If the sprite is inside a shadow cast by a light and the sprite has a lower z position than the light, the shadow affects how the sprite is lit. All of which I have done. My SKLightNode has a zPosition of 100 and all other nodes have lower zPositions.

I have tried any and all combinations of settings for lightingBitMask, shadowCastBitMask and shadowedBitMask but nothing has worked.

I am posting the isolated code which recreates my issue. The blue box casts a shadow but is not covered by the wall shadow. The purple box casts no shadow and is covered by the wall shadow.

The light responds to touch movement so feel free to move it around the screen. The project is in landscape mode.

What am I missing or not seeing?

#import "GameScene.h"

@implementation GameScene {
    SKSpriteNode *lightBulb;
}

-(void)didMoveToView:(SKView *)view {

    typedef NS_OPTIONS(uint32_t, Level1LightCategory)
    {
        CategoryLightPlayer            = 1 << 0,
    };

    SKSpriteNode *worldNode = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(1136, 640)];
    worldNode.zPosition = 10;
    //worldNode.position = CGPointMake(self.size.width/2, self.size.height/2);
    [self addChild:worldNode];

    lightBulb = [SKSpriteNode spriteNodeWithColor:[SKColor yellowColor] size:CGSizeMake(20, 20)];
    lightBulb.zPosition = 100;
    lightBulb.position = CGPointMake(50, 50);
    [worldNode addChild:lightBulb];

    SKLightNode *light = [[SKLightNode alloc] init];
    //light.zPosition = 100; // <- tried setting this again but to no effect
    light.categoryBitMask = CategoryLightPlayer;
    light.falloff = 0.3;
    light.ambientColor = [UIColor whiteColor];
    light.lightColor = [[UIColor alloc] initWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];
    light.shadowColor = [[UIColor alloc] initWithRed:0.0 green:0.0 blue:0.0 alpha:1.0];
    [lightBulb addChild:light];

    SKSpriteNode *wall = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(10, 300)];
    wall.zPosition = 50;
    wall.position = CGPointMake(500, 200);
    wall.lightingBitMask = CategoryLightPlayer;
    wall.shadowCastBitMask = CategoryLightPlayer;
    wall.shadowedBitMask = 0x00000000;
    [worldNode addChild:wall];

    SKSpriteNode *box0 = [SKSpriteNode spriteNodeWithColor:[SKColor blueColor] size:CGSizeMake(30, 30)];
    box0.zPosition = 40;
    box0.position = CGPointMake(800, 200);
    box0.lightingBitMask = CategoryLightPlayer;
    box0.shadowCastBitMask = CategoryLightPlayer;
    box0.shadowedBitMask = CategoryLightPlayer;
    [worldNode addChild:box0];

    SKSpriteNode *box1 = [SKSpriteNode spriteNodeWithColor:[SKColor purpleColor] size:CGSizeMake(30, 30)];
    box1.zPosition = 40;
    box1.position = CGPointMake(800, 300);
    box1.lightingBitMask = CategoryLightPlayer;
    //box1.shadowCastBitMask = CategoryLightPlayer;
    //box1.shadowedBitMask = CategoryLightPlayer;
    [worldNode addChild:box1];  
}

-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:self.scene];

    lightBulb.position = touchLocation;
}

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint touchLocation = [touch locationInNode:self.scene];

    lightBulb.position = touchLocation;
}

-(void)update:(CFTimeInterval)currentTime {
    //
}

@end
Lyricist answered 22/2, 2015 at 20:3 Comment(0)
L
10

I just heard back from Apple Developer Technical Support confirming this is a bug in SpriteKit (SKLightNode). Currently they do not know of a workaround for this issue but will update me if one is found.

As per their request, I have filed a bug report.

UPDATE April 22, 2015

SK engineering confirmed that SpriteKit doesn't support shadow casting and shadowed on the same object. There is no workaround at present. This issue will be addressed at some point.

UPDATE December 15, 2015

Received a new response from Apple regarding this issue.

Apple Developer Relations 14-Dec-2015 01:35 PM

There are no plans to address this.

We are now closing this report.

Lyricist answered 28/3, 2015 at 0:49 Comment(1)
I tested this out using Xcode 8.3.2 and it appears to have been fixed. I put your code into a sample project and casting a shadow from the wall over the blue box hides it as expected. The blue box also still casts its own shadow when not covered by the wall shadow. Perhaps this has been fixed in a recent SK update or maybe I'm misunderstanding your issue.Reedreedbird
E
-1

I'v found some workaround (not sure it's useful for all situation, but in some): You can make two (or more - up to 32 as we know) different light sources and assign you sprites to them one by one (or by some groups). Then shadows will combine as we need - correctly.

SKLightNode* light1 = [SKLightNode new];
SKLightNode* light2 = [SKLightNode new];

... // init them equally

light1.categoryBitMask = 1;
light2.categoryBitMask = 2;
...

SKSpriteNode* sprite1 = ...
SKSpriteNode* sprite2 = ...

sp1.shadowCastBitMask = 1;
sp1.shadowedBitMask = 1;
sp2.shadowCastBitMask = 2;
sp2.shadowedBitMask = 2;
Encage answered 23/4, 2015 at 10:53 Comment(2)
That is not a solution. The problem is for a node to cast a shadow, it cannot be shadowed at the same time. Creating 2 nodes does not solve it as the node casting a shadow will not disappear into a shadow.Lyricist
@Lyricist I think the workaround is that you duplicate your lights and sprites. So one light will make the sprite cast a shadow, and the other light will shadow the second sprite (using the same texture) - and they would be on top of each other. So one of the sprites cast a shadow isn't visible since it's underneath the second one - that does cast a shadow.Fluxion

© 2022 - 2024 — McMap. All rights reserved.