ทำไม Collision Layers จึงสำคัญ

Collision layers คือรากฐานของทุกการโต้ตอบด้าน physics ใน Godot 4 — การเคลื่อนที่, การตรวจจับการชน, raycasting, ทริกเกอร์ของ Area และอื่น ๆ การเข้าใจความแตกต่างระหว่าง Layer และ Mask เป็นสิ่งสำคัญอย่างยิ่ง และการเข้าใจผิดในเรื่องนี้เป็นหนึ่งในจุดที่สร้างความปวดหัวมากที่สุดทั้งสำหรับมือใหม่และนักพัฒนาที่มีประสบการณ์

Layer กับ Mask — แบบจำลองความคิด

ทุกวัตถุ physics ใน Godot มีคุณสมบัติ bitmask สองอย่าง:

กฎสำคัญ

A ชนกับ B เมื่อ Mask ของ A มี Layer ของ B หรือ Mask ของ B มี Layer ของ A เพียงฝ่ายเดียวที่ "มองเห็น" อีกฝ่ายก็เพียงพอที่จะทำให้เกิดการชนได้

การตั้งชื่อเลเยอร์ใน Project Settings

ก่อนที่จะเขียนโค้ดใด ๆ ให้ตั้งชื่อเลเยอร์ของคุณก่อน สิ่งนี้ทำให้ Inspector ใช้งานง่ายขึ้นมาก และป้องกันความสับสนเมื่อโปรเจกต์ของคุณเติบโตขึ้น

Project Settings > Layer Names > 2D Physics (หรือ 3D Physics):

Layer # ชื่อ วัตถุประสงค์
1Playerตัวละครผู้เล่น
2Enemyตัวศัตรูทั้งหมด
3Environmentกำแพง, พื้น, แพลตฟอร์ม
4Projectileกระสุน, ลูกธนู, คาถา
5Pickupไอเทม, เหรียญ, ชุดฟื้นฟูพลัง
6Triggerเซนเซอร์, โซนเกิด, จุดเช็กพอยต์

การตั้งค่าเลเยอร์ในโค้ด (GDScript)

Godot 4 มีสองวิธี — API แบบอิงค่า (แนะนำ) และการกำหนด bitmask โดยตรง:

# 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)

จุดพลาดของ Bitmask

หมายเลขเลเยอร์ใน set_collision_layer_value() เริ่มนับที่ 1 แต่ bitmask ที่อยู่เบื้องหลังเริ่มนับที่ 0 Layer 1 = bit 0 = ค่า 1, Layer 2 = bit 1 = ค่า 2, Layer 3 = bit 2 = ค่า 4 เมื่อไม่แน่ใจ ให้ใช้ API แบบอิงค่าเพื่อหลีกเลี่ยงข้อผิดพลาด

รูปแบบเลเยอร์ที่พบบ่อย

Platformer / Side-scroller

วัตถุ Layer Mask คำอธิบาย
Player12, 3, 5, 6ตรวจจับศัตรู, สภาพแวดล้อม, ไอเทม, ทริกเกอร์
Enemy21, 3ตรวจจับผู้เล่นและสภาพแวดล้อม
Environment3เชิงรับ — ถูกตรวจจับโดยวัตถุอื่น
Projectile42, 3ชนศัตรูและกำแพง
Pickup5เชิงรับ — ผู้เล่นเป็นฝ่ายตรวจจับ
Trigger61ตรวจจับเฉพาะผู้เล่น

Top-down / Roguelike

วัตถุ Layer Mask คำอธิบาย
Player12, 3, 5, 6ตรวจจับศัตรู, กำแพง, NPC, เซนเซอร์
Enemy21, 3ตรวจจับผู้เล่นและกำแพง
Wall3เชิงรับ
Bullet41, 2, 3ชนผู้เล่น, ศัตรู และกำแพง
NPC53ชนเฉพาะกำแพงเท่านั้น
Sensor61ตรวจจับการเข้ามาของผู้เล่น

เลเยอร์ของ Area2D / Area3D

Area ใช้ระบบ Layer/Mask เดียวกัน นอกจากนี้ยังมีคุณสมบัติแบบสวิตช์สองอย่าง:

สัญญาณ: area_entered กับ body_entered

body_entered จะทำงานเมื่อ PhysicsBody (CharacterBody, RigidBody, StaticBody) เข้าสู่ Area area_entered จะทำงานเมื่อArea อื่นเข้ามา ตรวจสอบให้แน่ใจว่าคุณเชื่อมต่อสัญญาณที่ถูกต้องสำหรับกรณีการใช้งานของคุณ

# 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

โหนด RayCast มีเพียง Mask เท่านั้น (ไม่มี Layer) เพราะมันเป็นตัวตรวจจับ ไม่ใช่วัตถุทางกายภาพ ตั้งค่า mask เพื่อกรองว่าเรย์สามารถชนอะไรได้บ้าง:

# 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

ตัวอย่างเชิงปฏิบัติ: Player – Enemy – Projectile

นี่คือการตั้งค่าเลเยอร์แบบสมบูรณ์สำหรับเกมแอ็กชันทั่วไป คอมเมนต์แสดงเหตุผลเบื้องหลังของแต่ละตัวเลือก:

# 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

เคล็ดลับ: การป้องกัน Friendly Fire

สังเกตว่า Player Bullet มาสก์เลเยอร์ 2 (Enemy) แต่ไม่มาสก์เลเยอร์ 1 (Player) และ Enemy Bullet มาสก์เลเยอร์ 1 (Player) แต่ไม่มาสก์เลเยอร์ 2 (Enemy) นี่คือวิธีป้องกัน friendly fire ด้วยการตั้งค่าเลเยอร์ล้วน ๆ — ไม่ต้องเขียนโค้ดเลย

เคล็ดลับการดีบัก

การเปลี่ยนแปลงในการย้ายจาก 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)
พารามิเตอร์ bit เริ่มนับที่ 0 พารามิเตอร์ layer เริ่มนับที่ 1
ใช้ได้ 20 เลเยอร์ ใช้ได้ 32 เลเยอร์

จุดพลาดในการย้าย

หากคุณกำลังพอร์ตโปรเจกต์ Godot 3 โปรดจำไว้ว่า set_collision_layer_bit(0, true) จะกลายเป็น set_collision_layer_value(1, true) ดัชนีจะเลื่อนไป +1 หากมองข้ามจุดนี้ เลเยอร์ทั้งหมดของคุณจะคลาดเคลื่อนไปหนึ่งตำแหน่ง

ข้อผิดพลาดที่พบบ่อย

1. ลืมตั้งค่า Mask

วัตถุของคุณมี Layer แต่ Mask ว่างเปล่า (เป็นศูนย์ทั้งหมด) วัตถุมีอยู่ในโลก physics แต่ไม่ตรวจจับอะไรเลย วัตถุอื่นที่มี mask ตรงกันจะยังตรวจจับมันได้ แต่ move_and_slide() บนวัตถุนี้จะทะลุผ่านทุกอย่าง

2. สับสนระหว่าง Layer กับ Mask

ตั้งค่า Layer ของ Player เป็น 2 (Enemy) แทนที่จะเป็น Mask ของมัน ตอนนี้ในมุมมองของ physics engine ผู้เล่นกลายเป็นศัตรูไปแล้ว จงจำไว้เสมอ: Layer = สิ่งที่ฉันเป็น, Mask = สิ่งที่ฉันสแกน

3. ใช้ค่า bitmask แทนหมายเลขเลเยอร์

เขียน set_collision_layer_value(4, true) โดยคิดว่ามันตั้งค่า bitmask เป็น 4 (เลเยอร์ 1+2) แต่ในความเป็นจริง มันเปิดใช้งานเลเยอร์ 4 API แบบอิงค่ารับหมายเลขเลเยอร์ ไม่ใช่ค่าบิต

4. ตรวจจับทางเดียวทั้งที่คาดหวังแบบสองทาง

วัตถุ A มาสก์เลเยอร์ของ B แต่ B ไม่ได้มาสก์เลเยอร์ของ A move_and_slide() บน A จะชนกับ B แต่ move_and_slide() บน B จะทะลุผ่าน A เพื่อให้โหนด CharacterBody สองตัวบล็อกกันและกัน ทั้งสองจะต้องมีเลเยอร์ของอีกฝ่ายอยู่ใน mask ของตน

ทำการตั้งค่า Physics อัตโนมัติด้วย Godot MCP Pro

เลิกสลับเช็กบ็อกซ์เลเยอร์ด้วยมือได้แล้ว ให้ AI กำหนดค่าการตั้งค่า collision ทั้งหมดของคุณภายในไม่กี่วินาที — รวมถึงการตั้งชื่อเลเยอร์, การกำหนด mask และการเพิ่ม raycast

รับ Godot MCP Pro — $15
เครื่องมือที่เกี่ยวข้อง: setup_collision set_physics_layers get_physics_layers get_collision_info add_raycast