Overview
In 2D games made with Godot Engine, lighting serves more than just decorative purposes. Properly configured light and shadow give scenes depth and atmosphere, guide player attention, and enhance the game's world.
This article focuses on PointLight2D node—the core of Godot's 2D lighting system—and shadow configuration, explaining practical best practices while addressing common pitfalls for beginners.
Fundamental Elements of 2D Lighting
Godot's 2D lighting works through the coordination of three main nodes.
| Element | Representative Node | Role |
|---|---|---|
| Light Source | PointLight2D, DirectionalLight2D | The entity that emits light into the scene. Determines light type like point or directional. |
| Occluder | LightOccluder2D | Defines shapes that block light and generate shadows. Independent of physics collision detection. |
| Subject | Sprite2D, TileMap, etc. | All nodes inheriting from CanvasItem. Affected by light—illuminated or hidden in shadow. |
How PointLight2D Works
PointLight2D is the most basic light source, radiating light in all directions from a single point. The light's shape and color can be finely adjusted in the inspector properties.
- Enabled: Enables/disables this light source.
- Texture: Texture defining light shape and falloff. Usually uses a radial gradient (
GradientTexture2D) that's white in the center and fades to black outward. - Color: Light color.
#FFFFFF(white) is default. - Energy: Light intensity. Higher values are brighter.
- Shadow > Enabled: Determines if this light source generates shadows. Enable only on necessary lights as it affects performance.
Tip: After adding
PointLight2Dto a scene, click[empty]on theTextureproperty in the inspector and select[New GradientTexture2D]. Then click the displayedGradientTexture2Dand changeFilltoRadial—that's all you need for a basic light source texture.
Shadow Boundaries: LightOccluder2D
Shadows aren't generated automatically. You need to tell Godot "what blocks light." That's the role of the LightOccluder2D node.
Add this node as a child of objects you want to cast shadows—walls, pillars, characters, etc. Then create a new OccluderPolygon2D resource in the Occluder property, and draw a polygon to block light by adding vertices from the editor's top toolbar.
Common Mistakes and Best Practices
2D lighting setup is intuitive, but several pitfalls exist.
| Mistake (Anti-pattern) | Problem | Solution (Best Practice) |
|---|---|---|
Not using LightOccluder2D | Even with PointLight2D's Shadow > Enabled on, no shadows generate because no occluders are defined. | Add LightOccluder2D to all objects (or their parents) that should cast shadows, and precisely define occlusion ranges with OccluderPolygon2D. |
Setting CanvasModulate to black | Entire scene goes pitch black, only lit areas visible. | CanvasModulate color is applied via multiply blend and becomes the color where no Light2D exists. Set slightly bright gray (e.g., #202020) instead of complete darkness to ensure minimum visibility. |
Too many OccluderPolygon2D vertices | Complex polygons increase per-frame shadow calculation cost, causing performance drops especially on mobile devices. | Create polygons with minimum necessary vertices to represent the occluder shape. For TileMap especially, set Occlusion in the TileSet editor rather than individual tiles, letting the engine optimize. |
| Enabling shadows on all lights | Calculates shadows even for decorative small lights or lights where performance doesn't matter, causing unnecessary load. | Enable shadows only on important lights that directly affect player gameplay (e.g., flashlight, main streetlights), and disable Shadow > Enabled for auxiliary atmospheric lights. |
Not utilizing Light Mask | All lights affect all objects, causing unintended lighting or missing optimization opportunities. | Use Range > Item Cull Mask (light side) and Visibility > Light Mask (object side) to layer lighting. |
Dynamic Lighting Control with GDScript
Using GDScript to dynamically control lights makes games more interactive.
Flashlight Implementation Example
A flashlight that follows the player with energy management.
# Player.gd (Expects child node named "Flashlight" as PointLight2D)
extends CharacterBody2D
@onready var flashlight: PointLight2D = $Flashlight
@export var max_light_energy: float = 1.0
@export var energy_drain_rate: float = 0.02 # Energy consumed per second
var current_energy: float
func _ready() -> void:
current_energy = max_light_energy
flashlight.enabled = false
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("toggle_light"):
flashlight.enabled = not flashlight.enabled
get_viewport().set_input_as_handled()
func _process(delta: float) -> void:
if not flashlight.enabled:
return
# Gradually decrease energy
current_energy -= energy_drain_rate * delta
if current_energy <= 0.0:
current_energy = 0.0
flashlight.enabled = false
# Change light intensity based on remaining energy
flashlight.energy = lerp(0.5, 1.5, current_energy / max_light_energy)
# Point flashlight toward mouse cursor
# Note: PointLight2D radiates light radially, so rotation doesn't change appearance.
# For a cone-shaped flashlight effect, you need a cone-shaped gradient image as Texture.
var mouse_pos = get_global_mouse_position()
flashlight.rotation = (mouse_pos - global_position).angle()
# Function to restore energy
func restore_energy(amount: float) -> void:
current_energy = min(current_energy + amount, max_light_energy)
if not flashlight.enabled and current_energy > 0:
flashlight.enabled = true
Comparison with DirectionalLight2D
While PointLight2D is a point light source, DirectionalLight2D is a parallel light source that shines from one direction across the entire scene, like sunlight or moonlight.
| Feature | PointLight2D | DirectionalLight2D |
|---|---|---|
| Light Shape | Radiates outward from a point | Falls parallel across entire scene |
| Primary Uses | Torches, light bulbs, magical lights—light sources at specific locations | Sunlight, moonlight, outdoor ambient light |
| Shadow Behavior | Shadows extend in all directions based on light position | Shadows extend in one direction based on light angle |
| Performance | Load increases with many placed | Usually one or two per scene. Relatively light. |
Usage Tips: For outdoor scenes, first set overall brightness and shadow direction with DirectionalLight2D, then supplement with PointLight2D for cave entrances, campfires, etc.
Summary
Understanding the combination of PointLight2D and LightOccluder2D is key to success with Godot's 2D lighting.
- PointLight2D: The light source itself. Adjust light appearance with texture and energy.
- LightOccluder2D: Boundaries for casting shadows. Essential node for generating shadows.
Master these fundamentals and your 2D games will evolve from flat monotony to worlds with depth woven from light and shadow. Recommended next steps include Normal Maps for more realistic lighting expression and custom shaders for extending light effects.