Почему слои коллизий важны

Слои коллизий — это основа всех физических взаимодействий в Godot 4 — движения, обнаружения попаданий, рейкастинга, триггеров Area и не только. Понимание разницы между Layer и Mask крайне важно, а её недопонимание — одна из самых распространённых проблем как для новичков, так и для опытных разработчиков.

Layer против Mask — ментальная модель

У каждого физического объекта в Godot есть два свойства-битмаски:

Ключевое правило

A сталкивается с B, когда Mask объекта A включает Layer объекта B ИЛИ Mask объекта B включает Layer объекта A. Достаточно, чтобы только одна сторона «видела» другую, чтобы произошло столкновение.

Именование слоёв в настройках проекта

Прежде чем писать какой-либо код, дайте имена своим слоям. Это делает инспектор гораздо удобнее в использовании и предотвращает путаницу по мере роста проекта.

Настройки проекта > Имена слоёв > 2D-физика (или 3D-физика):

№ слоя Имя Назначение
1PlayerПерсонаж игрока
2EnemyВсе тела врагов
3EnvironmentСтены, полы, платформы
4ProjectileПули, стрелы, заклинания
5PickupПредметы, монеты, аптечки
6TriggerДатчики, зоны спавна, контрольные точки

Установка слоёв в коде (GDScript)

Godot 4 предоставляет два подхода — API на основе значений (рекомендуется) и прямое присваивание битмаски:

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

Подвох с битмаской

Номера слоёв в set_collision_layer_value() отсчитываются с 1, но лежащая в основе битмаска отсчитывается с 0. Слой 1 = бит 0 = значение 1, слой 2 = бит 1 = значение 2, слой 3 = бит 2 = значение 4. В случае сомнений используйте API на основе значений, чтобы избежать ошибок.

Распространённые схемы слоёв

Платформер / сайд-скроллер

Объект Layer Mask Пояснение
Player12, 3, 5, 6Обнаруживает врагов, окружение, предметы, триггеры
Enemy21, 3Обнаруживает игрока и окружение
Environment3Пассивный — обнаруживается другими
Projectile42, 3Попадает по врагам и стенам
Pickup5Пассивный — его обнаруживает игрок
Trigger61Обнаруживает только игрока

Вид сверху / рогалик

Объект 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()

Маска коллизий RayCast

У узлов RayCast есть только Mask (нет Layer), потому что они являются детекторами, а не физическими телами. Задайте маску, чтобы отфильтровать, по чему может попадать луч:

# 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

Совет: предотвращение дружественного огня

Обратите внимание, что пули игрока маскируют слой 2 (Enemy), но не слой 1 (Player), а пули врагов маскируют слой 1 (Player), но не слой 2 (Enemy). Именно так вы предотвращаете дружественный огонь исключительно за счёт конфигурации слоёв — код не нужен.

Советы по отладке

Изменения при миграции с 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 пуста (все нули). Объект существует в физическом мире, но ничего не обнаруживает. Другие объекты с подходящими масками по-прежнему будут обнаруживать его, но move_and_slide() на этом объекте будет проходить сквозь всё.

2. Спутать Layer с Mask

Установить Layer игрока в 2 (Enemy) вместо его Mask. Теперь игрок является врагом с точки зрения физического движка. Всегда помните: Layer = то, чем я являюсь, Mask = то, что я сканирую.

3. Использовать значения битмаски вместо номеров слоёв

Написать set_collision_layer_value(4, true), думая, что это задаёт значение битмаски 4 (слои 1+2). На самом деле это включает слой 4. API на основе значений принимает номера слоёв, а не битовые значения.

4. Одностороннее обнаружение там, где ожидается двустороннее

Объект A маскирует слой B, но B не маскирует слой A. move_and_slide() на A столкнётся с B, но move_and_slide() на B пройдёт сквозь A. Чтобы два узла CharacterBody блокировали друг друга, оба должны иметь слой другого в своей маске.

Автоматизируйте настройку физики с Godot MCP Pro

Прекратите вручную переключать флажки слоёв. Позвольте ИИ настроить всю вашу систему коллизий за секунды — включая именование слоёв, назначение масок и добавление рейкастов.

Получить Godot MCP Pro — $15
Связанные инструменты: setup_collision set_physics_layers get_physics_layers get_collision_info add_raycast