Dlaczego warstwy kolizji są ważne
Warstwy kolizji są podstawą wszystkich interakcji fizycznych w Godot 4 — ruchu, wykrywania trafień, raycastingu, wyzwalaczy Area i nie tylko. Zrozumienie różnicy między Layer a Mask jest kluczowe, a jej niezrozumienie to jeden z najczęstszych problemów zarówno dla początkujących, jak i dla doświadczonych deweloperów.
Layer vs Mask — Model myślowy
Każdy obiekt fizyczny w Godot ma dwie właściwości typu bitmaska:
- Layer = „Istnieję na tych warstwach" (czym jestem)
- Mask = „Skanuję te warstwy" (co mogę widzieć / trafić)
Podstawowa zasada
A koliduje z B, gdy maska A zawiera warstwę B LUB maska B zawiera warstwę A. Wystarczy, że tylko jedna strona „widzi" drugą, aby doszło do kolizji.
Nazywanie warstw w ustawieniach projektu
Zanim napiszesz jakikolwiek kod, nazwij swoje warstwy. Dzięki temu inspektor jest znacznie łatwiejszy w użyciu i unikniesz zamieszania, gdy projekt się rozrośnie.
Ustawienia projektu > Nazwy warstw > Fizyka 2D (lub Fizyka 3D):
| Nr warstwy | Nazwa | Przeznaczenie |
|---|---|---|
| 1 | Player | Postać gracza |
| 2 | Enemy | Wszystkie ciała przeciwników |
| 3 | Environment | Ściany, podłogi, platformy |
| 4 | Projectile | Pociski, strzały, zaklęcia |
| 5 | Pickup | Przedmioty, monety, apteczki |
| 6 | Trigger | Czujniki, strefy spawnu, punkty kontrolne |
Ustawianie warstw w kodzie (GDScript)
Godot 4 oferuje dwa podejścia — API oparte na wartościach (zalecane) oraz bezpośrednie przypisanie bitmaski:
# Godot 4 API — value-based (1-indexed, recommended)
set_collision_layer_value(1, true) # I am on layer 1
set_collision_mask_value(2, true) # I detect layer 2
# Or use bitmask directly
collision_layer = 1 # Layer 1 only (bit 0)
collision_mask = 6 # Layers 2 and 3 (bits 1 + 2 = 6)
# Read current state
var on_layer_1: bool = get_collision_layer_value(1)
var scans_layer_2: bool = get_collision_mask_value(2)
Pułapka bitmaski
Numery warstw w set_collision_layer_value() są liczone od 1, ale leżąca u podstaw bitmaska jest liczona od 0. Warstwa 1 = bit 0 = wartość 1, warstwa 2 = bit 1 = wartość 2, warstwa 3 = bit 2 = wartość 4. W razie wątpliwości używaj API opartego na wartościach, aby uniknąć błędów.
Typowe schematy warstw
Platformówka / Side-scroller
| Obiekt | Layer | Mask | Wyjaśnienie |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | Wykrywa przeciwników, otoczenie, przedmioty, wyzwalacze |
| Enemy | 2 | 1, 3 | Wykrywa gracza i otoczenie |
| Environment | 3 | — | Pasywne — wykrywane przez innych |
| Projectile | 4 | 2, 3 | Trafia przeciwników i ściany |
| Pickup | 5 | — | Pasywne — gracz je wykrywa |
| Trigger | 6 | 1 | Wykrywa tylko gracza |
Top-down / Roguelike
| Obiekt | Layer | Mask | Wyjaśnienie |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | Wykrywa przeciwników, ściany, NPC, czujniki |
| Enemy | 2 | 1, 3 | Wykrywa gracza i ściany |
| Wall | 3 | — | Pasywne |
| Bullet | 4 | 1, 2, 3 | Trafia gracza, przeciwników i ściany |
| NPC | 5 | 3 | Koliduje tylko ze ścianami |
| Sensor | 6 | 1 | Wykrywa wejście gracza |
Warstwy Area2D / Area3D
Area używa tego samego systemu Layer/Mask. Dodatkowo posiada dwie właściwości przełączane:
-
monitoring— Gdytrue, ta Area aktywnie wykrywa inne obiekty do niej wchodzące. -
monitorable— Gdytrue, inne Area mogą wykryć tę Area.
Sygnały: area_entered vs body_entered
body_entered uruchamia się, gdy do Area wchodzi PhysicsBody (CharacterBody, RigidBody, StaticBody). area_entered uruchamia się, gdy wchodzi inna Area. Upewnij się, że podłączasz właściwy sygnał do swojego przypadku użycia.
# Pickup Area2D — detect when the Player body enters
func _ready() -> void:
body_entered.connect(_on_body_entered)
func _on_body_entered(body: Node2D) -> void:
if body.is_in_group("player"):
collect()
queue_free()
Maska kolizji RayCast
Węzły RayCast mają tylko Mask (bez Layer), ponieważ są detektorami, a nie ciałami fizycznymi. Ustaw maskę, aby filtrować to, w co promień może trafić:
# RayCast that only detects enemies (layer 2)
$RayCast2D.collision_mask = 2 # Layer 2 = Enemy
# Or use the value-based API:
$RayCast2D.set_collision_mask_value(1, false) # Ignore Player
$RayCast2D.set_collision_mask_value(2, true) # Detect Enemy
$RayCast2D.set_collision_mask_value(3, false) # Ignore Environment
# Line-of-sight ray that ignores projectiles
$LineOfSight.collision_mask = 0
$LineOfSight.set_collision_mask_value(1, true) # Player
$LineOfSight.set_collision_mask_value(3, true) # Environment
Praktyczny przykład: Player – Enemy – Projectile
Oto kompletna konfiguracja warstw dla typowej gry akcji. Komentarze pokazują tok rozumowania stojący za każdą decyzją:
# Player (CharacterBody2D)
# Layer: 1 (Player) — "I am the player"
# Mask: 2 (Enemy), 3 (Environment), 5 (Pickup)
# — I collide with enemies, walls, and can pick up items
# Enemy (CharacterBody2D)
# Layer: 2 (Enemy) — "I am an enemy"
# Mask: 1 (Player), 3 (Environment)
# — I collide with the player and walls
# Player Bullet (Area2D)
# Layer: 4 (Projectile) — "I am a projectile"
# Mask: 2 (Enemy), 3 (Environment)
# — I hit enemies and walls, but NOT the player who fired me
# Enemy Bullet (Area2D)
# Layer: 4 (Projectile) — "I am a projectile"
# Mask: 1 (Player), 3 (Environment)
# — I hit the player and walls, but NOT the enemy who fired me
Wskazówka: zapobieganie friendly fire
Zauważ, że pociski gracza maskują warstwę 2 (Enemy), ale nie warstwę 1 (Player), a pociski przeciwników maskują warstwę 1 (Player), ale nie warstwę 2 (Enemy). W ten sposób zapobiegasz friendly fire wyłącznie poprzez konfigurację warstw — bez żadnego kodu.
Wskazówki do debugowania
- Widoczne kształty kolizji: W edytorze przejdź do Debugowanie > Widoczne kształty kolizji, aby renderować wszystkie kształty kolizji w czasie działania. Natychmiast ujawnia to brakujące lub źle wyrównane collidery.
- Sprawdzenie w inspektorze: Wybierz węzeł i rozwiń Collision > Layer oraz Collision > Mask w inspektorze. Najedź na każdy bit, aby zobaczyć jego nazwę (jeśli nazwałeś je w ustawieniach projektu).
-
Wypisz w czasie działania:
print("Layer: ", collision_layer, " Mask: ", collision_mask), aby zweryfikować wartości bitmaski podczas rozgrywki.
Zmiany migracyjne Godot 3 → 4
| Godot 3 | Godot 4 |
|---|---|
set_collision_layer_bit(bit, value) |
set_collision_layer_value(layer, value) |
set_collision_mask_bit(bit, value) |
set_collision_mask_value(layer, value) |
Parametr bit był liczony od 0
|
Parametr layer jest liczony od 1
|
| 20 dostępnych warstw | 32 warstwy dostępne |
Pułapka migracji
Jeśli przenosisz projekt z Godot 3, pamiętaj, że set_collision_layer_bit(0, true) staje się set_collision_layer_value(1, true). Indeks przesuwa się o +1. Jeśli to przeoczysz, wszystkie Twoje warstwy będą przesunięte o jeden.
Częste błędy
1. Zapomnienie o ustawieniu maski
Twój obiekt ma warstwę, ale maska jest pusta (same zera). Obiekt istnieje w świecie fizyki, ale niczego nie wykrywa. Inne obiekty z pasującymi maskami wciąż go wykryją, ale move_and_slide() na tym obiekcie będzie przenikać przez wszystko.
2. Mylenie Layer z Mask
Ustawienie Layer gracza na 2 (Enemy) zamiast jego Mask. Teraz gracz jest przeciwnikiem z punktu widzenia silnika fizyki. Zawsze pamiętaj: Layer = czym jestem, Mask = co skanuję.
3. Używanie wartości bitmaski zamiast numerów warstw
Napisanie set_collision_layer_value(4, true) w przekonaniu, że ustawia ono wartość bitmaski 4 (warstwy 1+2). W rzeczywistości włącza to warstwę 4. API oparte na wartościach przyjmuje numery warstw, a nie wartości bitów.
4. Jednokierunkowe wykrywanie tam, gdzie oczekiwane jest dwukierunkowe
Obiekt A maskuje warstwę B, ale B nie maskuje warstwy A. move_and_slide() na A zderzy się z B, ale move_and_slide() na B przeniknie przez A. Aby dwa węzły CharacterBody wzajemnie się blokowały, oba muszą mieć warstwę drugiego w swojej masce.
Automatyzuj konfigurację fizyki z Godot MCP Pro
Przestań ręcznie przełączać pola wyboru warstw. Pozwól AI skonfigurować całe ustawienie kolizji w kilka sekund — w tym nazywanie warstw, przypisywanie masek i dodawanie raycastów.
Zdobądź Godot MCP Pro — $15setup_collision
set_physics_layers
get_physics_layers
get_collision_info
add_raycast