When the player is behind the wall, the light is 'covered'.
But when the player is in front of the wall, the light is normally displayed.

award Yes, I tried. But after I added occlusion and enabled shadow, the light disappeared as it "entered" the wall, both behind and in front of the wall.

Been messing around with this because I haven't really messed around with 2D lighting much before, especially not in Godot.

One thing I found which helps is adding normal mapping to your walls.

If you just paint your wall's normal map entirely #7F007F, then it will be "facing" directly (0, 1) and will only light from below. However if your light source is inside/behind the wall, it will still bleed weirdly 😕

Still looking for better solutions since I want to do this kinda stuff too.

Alright, something else that gets us not quite there:

A LightOccluder2D node will work better for our purposes than the tileset's occlusion shapes will.

Light behind:

Light in front:

The trick is to turn off Closed and set Cull Mode to ClockWise. That gets us a flat, mono-directional occluder.

Still dealing with the issue of lighting up cells with a higher Y when we're behind the wall, though 🤔

occluding.zip
202kB

This was very annoying to get to work.
The trick is to use two lighting/shadow layers, #1 for the ground and #2 for the walls
Straight-line LightOccluder2D along the base of the wall which only blocks #1 in both directions
Three-sided LightOccluder2D along the other edges of the wall which only blocks #2
A separate light for each light layer.
A separate TileMap for each light layer. Yup, can't use TileSet layers because they wont render separate shadow layers.
Then finally, because there is a bug where LightOccluder2Ds wont occlude lights inside of them, a script which literally just turns off light #2 while it's in the Area2D behind the wall.

🙃

    award My goodness thank you for all your hard work! I'm new to Godot so I did some simple searches before asking this question and came up with nothing. I was just worried that I was missing something so I complicated a simple question, but it seems like a real pain in the ass. I guess I should make the rest of the game first and treat this question as a bonus rather than a necessity.
    Maybe this would be much simpler in 3D mode, which is why so many 2D games use 3D mode these days.

      CrayonApe Yeah if my searches had come up with anything better I would have just pointed you in that direction 😂

      All the tutorials seem to only pertain to tiles which you cannot go "behind".

      I think there are a number of bugs currently being tracked on github which make this type of thing difficult to do at the moment.

      5 months later

      Necroing post in case it saves someone a few hours in the future.

      After trying to figure out this, came across another solution using shadow_item_cull_mask layers and shadow_item_cull_mask for the player's light. It works with 1 Tilemap along with an Area2D for detecting if the player is behind the wall or not. The process takes 3 steps.

      Step 1: Setting up tilemap.
      When setting up your tilemap, include the following layers.
      1) An occlusion layer with a light_mask set to layer 1 (adjust as needed).
      2) 3 tile layers:

      • Ground layer (z_index = 0)
      • Object layer (z_index = 2)
      • Ceiling layer (z_index = 3)

      After setting up your layer, Create a tileset and assign the occlusion mask to the TOP of the wall.

      It should look something like this:

      2) Setup collision and collision for tiles.

      Note: Adjust as needed. Rough example.

      Step 2: Setup PointLight2D for the player.
      1) Next step is to create a PointLight2D for the player.
      2) Create a texture of gradient. I chose a radial gradient.
      3) Important. Set shadow_item_cull_mask to layers 1 AND 2. Layer 1 will be for environment and 2 for player specific occlusion.3
      4) Important. Set the range_z_max to 3. This is the current layer of the ceiling. (i.e., the light shines on the wall from the front)

      End Result:

      You will notice the lamp from the player is cut off from the edges of the wall and shines against the ceiling portion of the wall. This is due to Item Cull Mask Layer 1 matching the occlusion layer from the TileMap as well as the range being the same layer as the ceiling layer.

      Step 3: Create LightOccluder2D node for when player is behind the wall.
      1) Create a LightOccluder2D around the bottom of the wall.
      2) Set the polygon around the base of the wall.
      3) Important. Set the occluder_light_mask to layer 2. This causes it to occlude light from the player's light but not environment lights.

      End Result:

      Note: I angled the occluder to make it more of a gradual change. Play with whatever shape works best.

      Finally for step 3, set the visibility to false. You'll use the Area2D to toggle the occluder on and off.

      Step 4: Area2D for detecting behind the wall and code stuffs.
      1) Create an Area2D to detect if the player is behind the wall.
      2) Set the area above the collision area for the wall using a CollisionPolygon2D (shapes work too).

      Result:

      3) Attach the body_entered and body_exited signals to a scene script.
      4) Setup the script so that:
      a) On entering the area behind the wall:

      • Set TileMap z_index to 1 (this prevents the light shining on the wall from behind due to the range of 3 we set).
      • PointLight2D from step 2, removes the shadow_item_cull_mask for layer 1 (the environment light occluders).
      • Sets the LightOccluder2D from step 4 to visible to restrict the player's light bleeding through the wall.
        b) On existing the area behind the wall:
      • Set TileMap z_index back to 0.
      • PointLight2D from step 2, adds back shadow_item_cull_mask for layer 1 (the environment light occluders).
      • Sets the LightOccluder2D from step 4 visible to false to allow the player's light to shine against the wall when in front.

      Example Script:

      func _on_area_2d_body_entered(_body):
      	%TileMap.z_index = 1
      	%WallLightOccluder2D.visible = true
      	var lamp_light:PointLight2D = %LampLight
      	var cull_mask = lamp_light.shadow_item_cull_mask
      	if cull_mask & (cull_mask << 1):
      		lamp_light.shadow_item_cull_mask = cull_mask - 1
      		print("cull mask ",cull_mask," changed to:",lamp_light.shadow_item_cull_mask)
      
      
      func _on_area_2d_body_exited(_body):
      	%TileMap.z_index = 0
      	%WallLightOccluder2D.visible = false
      	var lamp_light:PointLight2D = %LampLight
      	var cull_mask = lamp_light.shadow_item_cull_mask
      	if not (cull_mask & (cull_mask << 1)):
      		lamp_light.shadow_item_cull_mask = cull_mask + 1
      		print("cull mask ",cull_mask," changed to:",lamp_light.shadow_item_cull_mask)

      Note: LampLight is the player's light. Also, the cull_mask logic uses bitwise operators. Basically checks if the layer is active since layers are stored as a bitwise (int).

      Visual of how the z_index works for the TileMap:

      Final Result:

      Player's light will shine on wall from front and then from the sides from behind.

      TLDL; Use an Area2D to shift between occluders for in front and behind wall. Use shadow range and light mask layer.