This is an old question, but I ran into the same issue and had the same incomplete understanding of the Godot 2D lighting system prompting it. And the one posted answer is wrong, or at least woefully incomplete.
The key concept is that the 2D light system is a physically-inspired model of light in a completely 2D space, optionally allowing line segments in the space (and hence polygons composed of them) to block light and cast shadows when the light enters from one or both sides of the line segment. Nothing in the light system has any concept of tiles, and every way of integrating tiles with the engine 2D light system -- including built-in TileMap
-- must work in terms of tiling occlusion polygons, which necessarily results in walls covered by tile-sized occlusion polygons casting shadows on each other.
To see why there probably couldn't be an engine-based solution within the existing light model, consider looking down a line of wall tiles. If the line of tiles is viewed obliquely, with all the tiles directly visible, then none of the tiles should cast shadows on each other. But if the line of tiles is viewed head-on, then the first tile should cast a shadow on all the subsequent tiles. I do not believe it is possible to implement this behavior using only the primitives provided by the Godot 2D light system and no additional code.
So, what I believe to be the best/real solution:
- Implement tile-aware visibility behavior in your own game logic. Using 2D shadowcasting for the visibility algorithm allows the result to be integrated into the engine 2D light system by placing occlusion line segments at the boundaries between visible and non-visible tiles. I have managed to implement this and get exactly the effect I want.
Partial/alternative solutions, which may work for some people:
- Work entirely within the engine 2D lighting system by designing your tiles to incorporate hard visibility barriers. For walls for example, this could involve designing wall tiles to have a visibility barrier down the center/cornering for every possible orientation; or splitting walls into two sets of tiles, with one including the majority of the visible portions and the other primarily/exclusively just blocking light.
- Give the wall texture tiles a different light mask from their associated occluders, and set the shadow mask of all lights not to include the wall textures. This prevents any wall from casting shadows on any wall, which leaves all walls visible, but does cast shadows on other objects with the appropriate light mask. (This is what I believe Wibbly is describing in their posted answer.)
- Use a CanvasItem shader with a light processor function to redirect undesired shadows. This one is purely speculative, as I haven't spent enough time to see if could be made to work. Within CanvasItem shaders, the
light()
function can modify the value of SHADOW_VERTEX
to redirect a shadow which would land on a given vertex to land on another vertex instead. The problem is knowing which shadows are undesired, which probably devolves once more to implementing tile-based visibility, only now either doing it in a shader or communicating it to a shader.
Hopefully that definitively answers this question!
Update: I've created a demo project demonstrating this technique -- llasram/godot-visibility-demo.