Mengapa Collision Layer Penting
Collision layer adalah fondasi dari semua interaksi fisika di Godot 4 — pergerakan, deteksi tumbukan, raycasting, pemicu area, dan lainnya. Memahami perbedaan antara Layer dan Mask sangatlah penting, dan salah memahaminya adalah salah satu titik pusing yang paling umum baik bagi pemula maupun developer berpengalaman.
Layer vs Mask — Model Berpikir
Setiap objek fisika di Godot memiliki dua properti bitmask:
- Layer = "Aku ada di layer ini" (siapa aku)
- Mask = "Aku memindai layer ini" (apa yang bisa kulihat / kukenai)
Aturan Utama
A bertumbukan dengan B ketika mask A memuat layer B ATAU mask B memuat layer A. Hanya satu sisi yang perlu "melihat" sisi lainnya agar tumbukan terjadi.
Menamai Layer di Project Settings
Sebelum menulis kode apa pun, namai layer-mu. Ini membuat Inspector jauh lebih mudah digunakan dan mencegah kebingungan seiring bertumbuhnya proyekmu.
Project Settings > Layer Names > 2D Physics (atau 3D Physics):
| Layer # | Nama | Tujuan |
|---|---|---|
| 1 | Player | Karakter pemain |
| 2 | Enemy | Semua body musuh |
| 3 | Environment | Dinding, lantai, platform |
| 4 | Projectile | Peluru, panah, mantra |
| 5 | Pickup | Item, koin, paket kesehatan |
| 6 | Trigger | Sensor, zona spawn, checkpoint |
Mengatur Layer di Kode (GDScript)
Godot 4 menyediakan dua pendekatan — API berbasis nilai (direkomendasikan) dan penetapan bitmask langsung:
# 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)
Jebakan Bitmask
Nomor layer bersifat berbasis 1 pada set_collision_layer_value(), tetapi bitmask yang mendasarinya bersifat berbasis 0. Layer 1 = bit 0 = nilai 1, Layer 2 = bit 1 = nilai 2, Layer 3 = bit 2 = nilai 4. Jika ragu, gunakan API berbasis nilai untuk menghindari kesalahan.
Skema Layer yang Umum
Platformer / Side-scroller
| Objek | Layer | Mask | Penjelasan |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | Mendeteksi musuh, environment, pickup, trigger |
| Enemy | 2 | 1, 3 | Mendeteksi pemain dan environment |
| Environment | 3 | — | Pasif — dideteksi oleh yang lain |
| Projectile | 4 | 2, 3 | Mengenai musuh dan dinding |
| Pickup | 5 | — | Pasif — pemain mendeteksinya |
| Trigger | 6 | 1 | Mendeteksi pemain saja |
Top-down / Roguelike
| Objek | Layer | Mask | Penjelasan |
|---|---|---|---|
| Player | 1 | 2, 3, 5, 6 | Mendeteksi musuh, dinding, NPC, sensor |
| Enemy | 2 | 1, 3 | Mendeteksi pemain dan dinding |
| Wall | 3 | — | Pasif |
| Bullet | 4 | 1, 2, 3 | Mengenai pemain, musuh, dan dinding |
| NPC | 5 | 3 | Bertumbukan hanya dengan dinding |
| Sensor | 6 | 1 | Mendeteksi masuknya pemain |
Layer Area2D / Area3D
Area menggunakan sistem Layer/Mask yang sama. Selain itu, mereka memiliki dua properti toggle:
-
monitoring— Jikatrue, Area ini secara aktif mendeteksi objek lain yang memasukinya. -
monitorable— Jikatrue, Area lain dapat mendeteksi Area ini.
Signal: area_entered vs body_entered
body_entered terpicu ketika sebuah PhysicsBody (CharacterBody, RigidBody, StaticBody) memasuki Area. area_entered terpicu ketika Area lain memasukinya. Pastikan kamu menghubungkan signal yang tepat untuk kasus penggunaanmu.
# 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()
Collision Mask RayCast
Node RayCast hanya memiliki Mask (tanpa Layer) karena mereka adalah detektor, bukan body fisik. Atur mask untuk memfilter apa yang bisa dikenai oleh sinar:
# 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
Contoh Praktis: Player – Enemy – Projectile
Berikut adalah penyiapan layer lengkap untuk game aksi pada umumnya. Komentar menunjukkan alasan di balik setiap pilihan:
# 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
Tips: Mencegah Friendly Fire
Perhatikan bahwa Player Bullet me-mask layer 2 (Enemy) tetapi tidak layer 1 (Player), dan Enemy Bullet me-mask layer 1 (Player) tetapi tidak layer 2 (Enemy). Inilah cara mencegah friendly fire murni melalui konfigurasi layer — tanpa perlu kode.
Tips Debugging
- Visible Collision Shapes: Di editor, buka Debug > Visible Collision Shapes untuk merender semua collision shape saat runtime. Ini langsung mengungkap collider yang hilang atau tidak sejajar.
- Pemeriksaan Inspector: Pilih sebuah node dan buka Collision > Layer dan Collision > Mask di Inspector. Arahkan kursor ke tiap bit untuk melihat namanya (jika kamu menamainya di Project Settings).
-
Cetak saat runtime:
print("Layer: ", collision_layer, " Mask: ", collision_mask)untuk memverifikasi nilai bitmask selama gameplay.
Perubahan Migrasi 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) |
Parameter bit berbasis 0
|
Parameter layer bersifat berbasis 1
|
| 20 layer tersedia | 32 layer tersedia |
Jebakan Migrasi
Jika kamu mem-porting proyek Godot 3, ingat bahwa set_collision_layer_bit(0, true) menjadi set_collision_layer_value(1, true). Indeksnya bergeser +1. Kalau terlewat, semua layer-mu akan meleset satu.
Kesalahan Umum
1. Lupa mengatur Mask
Objekmu memiliki Layer tetapi Mask-nya kosong (semua nol). Objek itu ada di dunia fisika tetapi tidak mendeteksi apa pun. Objek lain dengan mask yang cocok tetap akan mendeteksi -nya, tetapi move_and_slide() pada objek ini akan menembus segalanya.
2. Mengacaukan Layer dengan Mask
Mengatur Layer pemain ke 2 (Enemy) alih-alih Mask-nya. Kini pemain adalah musuh menurut engine fisika. Selalu ingat: Layer = siapa aku, Mask = apa yang kupindai.
3. Menggunakan nilai bitmask alih-alih nomor layer
Menulis set_collision_layer_value(4, true) dengan anggapan itu mengatur nilai bitmask 4 (layer 1+2). Kenyataannya, ia mengaktifkan layer 4. API berbasis nilai menerima nomor layer, bukan nilai bit.
4. Deteksi satu arah padahal diharapkan dua arah
Objek A me-mask layer B tetapi B tidak me-mask layer A. move_and_slide() pada A akan bertumbukan dengan B, tetapi move_and_slide() pada B akan menembus A. Agar dua node CharacterBody saling menghalangi, keduanya perlu memiliki layer satu sama lain di mask-nya.
Otomatiskan Penyiapan Fisika dengan Godot MCP Pro
Berhentilah mengaktifkan kotak centang layer secara manual. Biarkan AI mengonfigurasi seluruh penyiapan collision-mu dalam hitungan detik — termasuk menamai layer, menetapkan mask, dan menambahkan raycast.
Dapatkan Godot MCP Pro — $15setup_collision
set_physics_layers
get_physics_layers
get_collision_info
add_raycast