← 목차
Godot 4.7 · MultiMeshInstance3D

수천 그루를 한 번에 그린다
그런데 부딪히지는 않는다

같은 메시를 드로우콜 1회로 그리는 장치. 씬을 처음부터 만드는 법부터, 뿌린 것들이 왜 물리 세계에 없는지, 그리고 콜리전을 붙이는 여섯 가지 방법까지 — 전부 직접 돌려 확인했다.

4.7.2.stable 실측 스크린샷은 직접 렌더 엔진·애드온 소스 확인 상세 문서 · multimesh-3d.md

① 같은 화면, 다른 비용

나무 300그루를 놓은 같은 장면이다. 화면은 똑같다. 다른 것은 노드 수와 드로우콜, 그리고 물리의 유무다.

MeshInstance3D 300개로 그린 숲 MultiMeshInstance3D 1개로 그린 같은 숲 레이캐스트가 나무를 그대로 통과하는 장면

구조 — 노드와 리소스가 두 층이다

처음 만지면 여기서 한 번 헷갈린다.

MultiMeshInstance3D          노드 — 씬의 어디에 놓을지, 어느 레이어에 그릴지
  └ multimesh: MultiMesh     리소스 — 메시 1개 + 인스턴스 N개의 변환 배열
        ├ mesh: Mesh              무엇을 그리는가 (모든 인스턴스가 공유)
        ├ transform_format        TRANSFORM_3D / TRANSFORM_2D
        ├ instance_count          몇 개인가
        ├ visible_instance_count  그중 몇 개를 그릴 것인가 (−1 = 전부)
        ├ use_colors              인스턴스마다 색을 다르게 줄 것인가
        └ use_custom_data         셰이더로 넘길 4채널 값을 쓸 것인가

MultiMeshInstance3D 를 씬에 추가하면 multimesh 칸이 <empty> 다. Populate Surface 를 쓰면 자동으로 채워지고, 코드로 만들 때는 MultiMesh.new() 를 넣어 줘야 한다.

② 씬을 처음부터 만든다

Populate Surface 를 쓰려면 노드 셋이 한 씬 안에 있어야 한다. 하나라도 빠지면 다이얼로그가 열리지 않거나 거절당한다.

역할노드 타입하는 일
Target Surface🛑 MeshInstance3D인스턴스가 놓일 바닥. 면이 있어야 한다
Source Mesh🛑 MeshInstance3D복제할 것 (나무 1그루)
결과MultiMeshInstance3D뿌려진 결과가 담긴다
1. Scene ▸ New Scene ▸ 3D Scene            루트가 Node3D 로 생긴다
2. 루트 우클릭 ▸ Add Child Node
     MeshInstance3D        → Inspector ▸ Mesh ▸ New PlaneMesh (30 × 30)   … 바닥
     DirectionalLight3D                                                   … 빛
     Camera3D                                                             … 시점
     MultiMeshInstance3D                                                  … 결과
3. 바닥에 콜리전을 준다 (캐릭터가 서 있으려면 필요하다)
     바닥 선택 ▸ 툴바 Mesh ▸ Create Collision Shape
       Placement = Static Body Child · Type = Trimesh → Create
4. 뿌릴 나무를 씬에 넣는다   ← 여기서 대부분 막힌다
5. MultiMeshInstance3D 선택 ▸ 툴바 MultiMesh ▸ Populate Surface
6. Ctrl/Cmd + S 로 저장

🛑 4단계 — .glb 를 넣었다면 한 단계가 더 있다

.glb 를 FileSystem 에서 뷰포트로 끌어다 놓으면 씬 트리에 이렇게 들어온다. 이 노드를 Source Mesh 로 고르면 거절당한다 — 타입이 Node3D 이기 때문이다.

Node3D └── grass-trees2 🎬 씬 인스턴스. 이 노드의 타입은 Node3D 다
Surface source is invalid (not a MeshInstance3D).

메시는 그 안쪽에 있는데, 인스턴스는 기본적으로 내부가 접혀 있어 보이지 않는다. 꺼내는 방법은 둘이다.

방법어떻게결과언제
Editable Children 인스턴스 우클릭 ▸ Editable Children 체크 자식이 펼쳐진다. 이름이 다른 색으로 표시된다(원본 씬 소유라는 뜻) 원본 .glb 와의 연결을 유지하고 싶을 때
Make Local 인스턴스 우클릭 ▸ Make Local 인스턴스 연결이 끊기고 평범한 노드 묶음이 된다 이 씬에서만 자유롭게 고칠 때

Editable Children 을 켜면 이렇게 된다:

Node3D ├── WorldEnvironment ├── grass-trees2 🎬 인스턴스 (Editable Children ✅) │ └── grass-trees ← MeshInstance3D. 이것을 Source Mesh 로 고른다 ├── MeshInstance3D ← 바닥 (Target Surface) │ └── StaticBody3D │ └── CollisionShape3D ├── MultiMeshInstance3D ← 결과가 담길 곳 └── Camera3D

이름 색이 다른 이유 — 그 노드는 원본 씬이 소유한다. 값을 바꾸면 되돌리기 화살표가 뜨고, 원본 .glb 를 다시 임포트하면 덮어써진다.

🛑 MultiMeshInstance3D 를 인스턴스 안에 넣지 않는다

Editable Children 을 켜 두면 인스턴스 안에도 노드를 추가할 수 있다. 하지만 그렇게 하지 않는다.

🛑 이렇게 하지 않는다

Node3D
└── grass-trees2  (인스턴스)
    ├── grass-trees
    └── MultiMeshInstance3D  ← 안쪽

✅ 이렇게 한다

Node3D
├── grass-trees2  (인스턴스)
│   └── grass-trees   ← Source 로만
└── MultiMeshInstance3D  ← 루트 아래
인스턴스 안의 노드는 원본 씬 구조에 얹힌 덧붙임이라, 원본 .glb 를 다시 임포트하거나 구조가 바뀌면 자리를 잃거나 사라질 수 있다
인스턴스의 transform 이 결과 전체에 곱해진다 — 나무 한 그루를 옮겼을 뿐인데 뿌린 숲이 통째로 움직인다
나중에 Source Mesh 를 지우고 싶어도 MultiMesh 가 그 안에 있어 같이 지워진다

결과 노드는 항상 바깥에, 뿌릴 좌표의 기준이 되는 자리에 둔다. 다 뿌린 뒤 Source Mesh 노드는 지워도 된다 — 변환 배열은 이미 MultiMesh 리소스 안에 복사됐다.

③ 에디터로 뿌린다 — Populate Surface

1. 씬 트리에서 MultiMeshInstance3D 를 클릭해 선택한다
     🛑 선택하지 않으면 툴바에 MultiMesh 메뉴가 나타나지 않는다
2. 3D 뷰포트 상단 툴바 ▸ MultiMesh  → 클릭
3. Populate Surface
4. Target Surface  ▸ [..] → 바닥 MeshInstance3D → OK
5. Source Mesh     ▸ [..] → 나무 MeshInstance3D → OK
6. Amount 등을 정한다
7. Populate
필드기본값하는 일
Target Surface삼각형을 면적 가중치로 뽑아 그 위 임의의 점에 놓는다
Source Mesh복제할 메시
Mesh Up AxisY-Axis소스 메시의 위쪽 축. 나무가 누워서 심기면 여기
Random Rotation0위 축 기준 무작위 회전 — 복제한 티를 지운다
Random Tilt0옆으로 기울이기
Random Scale0.0크기 무작위 폭
Scale1.0기본 크기
Amount128개수

🛑 막히는 곳과 원인

증상원인
툴바에 MultiMesh 메뉴가 없다MultiMeshInstance3D선택하지 않았다
Surface source is invalid (not a MeshInstance3D).고른 노드가 MeshInstance3D 가 아니다 → ② 의 Editable Children
Surface source is invalid (no faces).바닥 메시에 면이 없다
No mesh source specified…Source Mesh 를 안 골랐거나 그 노드의 Mesh 가 비어 있다
나무가 누워서 심긴다Mesh Up Axis 를 소스 메시에 맞춘다
다시 Populate 했더니 더해지지 않는다정상이다. 덮어쓴다
뿌린 나무가 전부 같은 방향Random Rotation 을 올린다

실측 — 나무 3그루를 뿌렸을 때

뿌리기 전뿌린 뒤변화
Objects2324+1 (노드 1개)
Primitives12,04518,310+6,265 (삼각형은 실제로 늘어난다)
Draw Calls2324+1

나무가 3그루든 300그루든 드로우콜은 +1 이다. 이것이 MultiMesh 를 쓰는 유일한 이유다. 반대로 삼각형은 전혀 줄지 않는다.

④ 🛑 콜리전은 복제되지 않는다

원본 나무에 Mesh ▸ Create Collision Shape ▸ CapsuleStaticBody3D 를 만들어 두어도, 뿌린 인스턴스에는 충돌이 생기지 않는다. 버그가 아니다. 이유는 하나다 —

MultiMesh 변환 배열 N개 ✕ 연결 없음 RenderingServer 인스턴스는 여기에만 있다 PhysicsServer3D 여기에는 아무것도 없다 보인다 통과한다

MultiMesh렌더링 서버의 자료구조다. 물리 서버는 CollisionObject3D 를 상속한 노드만 안다. 그리고 Populate SurfaceMesh 리소스만 복사하므로, 원본 씬 트리에 매달린 StaticBody3D 는 구조적으로 따라올 수가 없다. 에디터 플러그인 소스에도 콜리전을 만드는 코드가 한 줄도 없다.

godot --headless --path . -s res://tests/multimesh_collision_probe.gd
=== Godot 4.7.2-stable (official) ===

[A] MultiMeshInstance3D 인스턴스에 물리가 있는가
  instance_count = 3 · 노드 수 = 1
  ✅ 인스턴스 0 (x=0)  에 레이 — 기대 충돌없음 / 실제 충돌없음
  ✅ 인스턴스 1 (x=10) 에 레이 — 기대 충돌없음 / 실제 충돌없음
  ✅ 인스턴스 2 (x=20) 에 레이 — 기대 충돌없음 / 실제 충돌없음

[B] 원본에 StaticBody3D 를 두면 인스턴스에도 생기는가
  ✅ 원본에 레이                    — 기대 충돌함   / 실제 충돌함
  ✅ MultiMesh 사본 (x=-40) 에 레이 — 기대 충돌없음 / 실제 충돌없음
  ✅ MultiMesh 사본 (x=-60) 에 레이 — 기대 충돌없음 / 실제 충돌없음

[C] GridMap + MeshLibrary(shape 포함) 는 콜리전이 자동으로 생기는가
  ✅ 셀 0 (x=0) 에 레이 — 기대 충돌함 / 실제 충돌함
  ✅ 셀 1 (x=4) 에 레이 — 기대 충돌함 / 실제 충돌함
  ✅ 셀 2 (x=8) 에 레이 — 기대 충돌함 / 실제 충돌함

실패 0 건

[B] 가 답이다 — 원본만 충돌하고 사본은 통과한다.

⑤ 콜리전을 붙이는 여섯 가지 방법

엔진 내장에는 "원본 콜리전을 인스턴스에 자동 복제" 하는 버튼이 없다. 제안 #10828 은 2024-09-26 에 열린 뒤 아직 open 이고 담당자도 없다.

먼저 두 갈래를 구분해야 한다.

[한꺼번에 붙인다]  인스턴스 N개에 대응하는 shape N개를, 바디 1개에 몰아 넣는다
                   → 방법 A · B · C · D.  노드 수가 폭발하지 않는다

[하나씩 붙인다]   인스턴스마다 StaticBody3D 를 따로 만든다
                   → 방법 E.  MultiMesh 를 쓰는 의미가 절반 사라진다
#방법클릭만으로?드로우콜만드는 노드에디터에 보이나언제
AProtonScatter + Keep Static Colliders✅ 체크박스 하나10자유 배치 + 충돌
BGridMap + MeshLibrary (내장)✅ 된다배칭됨0격자에 놓아도 되는 것
C스크립트 — StaticBody3D 1개 + CollisionShape3D N개❌ 코드1N+1디버그 뷰로 보고 싶을 때
D스크립트 — PhysicsServer3D 직접❌ 코드10인스턴스가 아주 많을 때
E충돌이 필요한 것만 개별 노드개수만큼N큰 나무 수십 그루
F콜리전을 안 준다 / 넓게 뭉뚱그린다10~1풀·꽃·자갈·먼 배경

C. StaticBody3D 1개 + CollisionShape3D N개

가장 이해하기 쉽고, 에디터 디버그 뷰에서 눈으로 확인된다.

노드가 보인다레이어 한 번에 노드 N+1개
# MultiMeshInstance3D 아래에 바디 하나를 두고, 인스턴스마다 shape 노드를 붙인다
var body := StaticBody3D.new()
mmi.add_child(body)                       # MultiMesh 와 같은 좌표계에 둔다

var shape := CapsuleShape3D.new()         # 🛑 shape 리소스는 하나만 만들어 공유한다
shape.radius = 0.45
shape.height = 3.2

for i in mmi.multimesh.instance_count:
    var col := CollisionShape3D.new()
    col.shape = shape                     # 같은 리소스를 N개가 함께 쓴다 (메모리 1벌)
    col.transform = mmi.multimesh.get_instance_transform(i)
    body.add_child(col)

D. PhysicsServer3D 에 직접 등록

노드를 하나도 만들지 않는다. ProtonScatter 가 내부에서 쓰는 방식이다.

노드 0개수천 개도 가볍다 디버그 뷰에 안 보인다RID 직접 해제
# 바디 하나를 물리 서버에 직접 만든다
var body_rid := PhysicsServer3D.body_create()
PhysicsServer3D.body_set_mode(body_rid, PhysicsServer3D.BODY_MODE_STATIC)
PhysicsServer3D.body_set_space(body_rid, get_world_3d().space)
PhysicsServer3D.body_set_state(
    body_rid, PhysicsServer3D.BODY_STATE_TRANSFORM, mmi.global_transform)

# shape 도 하나만 만들어 N번 재사용한다
var shape_rid := PhysicsServer3D.capsule_shape_create()
PhysicsServer3D.shape_set_data(shape_rid, {"radius": 0.45, "height": 3.2})

for i in mmi.multimesh.instance_count:
    PhysicsServer3D.body_add_shape(
        body_rid, shape_rid, mmi.multimesh.get_instance_transform(i))

# 🛑 RID 는 직접 지운다. 노드가 아니라서 queue_free() 가 챙겨 주지 않는다
# PhysicsServer3D.free_rid(body_rid)
# PhysicsServer3D.free_rid(shape_rid)

레이어를 잊지 말 것body_create() 의 기본 레이어는 1이다. PhysicsServer3D.body_set_collision_layer(body_rid, 원하는_비트) 를 반드시 부른다.

실측 — C·D 둘 다 동작한다

godot --headless --path . -s res://tests/mm_add_collision_probe.gd
=== Godot 4.7.2-stable (official) · MultiMesh 에 콜리전 붙이기 실측 ===

[기준선] MultiMesh 만 놓았을 때
  ✅ 레이 24발 중  0발 명중 · 만들어진 노드  0개

[방법 C] StaticBody3D 1개 + CollisionShape3D N개
         노드가 보이고 Debug ▸ Visible Collision Shapes 로 확인된다
  ✅ 레이 24발 중 24발 명중 · 만들어진 노드 25개

[방법 D] PhysicsServer3D 에 shape 을 직접 등록 (ProtonScatter 방식)
         노드가 0개다. 그래서 Debug 뷰에도 안 보인다
  ✅ 레이 24발 중 24발 명중 · 만들어진 노드  0개

실패 0 건

🛑 방법 E 를 고를 때의 판단
"인스턴스마다 StaticBody3D 를 하나씩" 은 MultiMesh 를 쓰는 의미를 절반 없앤다. 드로우콜은 1로 남지만 노드 수·메모리·씬 트리 부담이 개별 배치와 같아진다. 충돌이 필요한 것이 수십 개뿐이라면 애초에 MultiMesh 를 쓰지 말고 개별 노드로 두는 편이 낫다.

⑥ ProtonScatter 완전 안내

📘 클릭 순서만 따라 하려면 ProtonScatter 처음부터 끝까지 — 설치부터 Keep Static Colliders 까지 10단계로 나눠 적었다. 이 절은 요약이다.

"원본 씬에 콜리전을 넣어 두면 뿌린 전부에 자동으로 생긴다" 를 체크박스 하나로 해 주는 유일한 도구다. 엔진 기능이 아니라 애드온 (HungryProton/scatter)이다.

왜 되는가 — 소스가 근거다

MultiMesh 인스턴스를 채우는 바로 그 루프가, 같은 Transform3D 로 물리 서버에 shape 을 등록한다. 즉 ⑤ 의 방법 D 를 애드온이 대신 해 주는 것이다.

# scatter.gd:74-76 — 프로퍼티 선언
## If enabled, creates static collision shapes for scattered objects.
## Uses the Physics server directly instead of creating actual collision nodes
@export var keep_static_colliders := false

# scatter.gd:45-51 — render_mode 의 의미
## Use Instancing (0): Uses MultiMesh instances for efficient rendering…
## Create Copies (1): Creates individual node copies for each scattered object.
## Use Particles (2): Uses GPU particles system for very large numbers of objects.

# scatter.gd:448-450 — 결정적. 렌더와 물리가 같은 t 를 쓴다
t = item.process_transform(transforms.list[offset + i])
mmi.multimesh.set_instance_transform(i, t)   # 렌더
_create_collision(static_body, t)            # 물리

# scatter.gd:609-611 — MultiMesh 모드(0)에서 작동한다. 끄는 것은 1(Create Copies)뿐
func _create_collision(body: StaticBody3D, t: Transform3D) -> void:
	if not keep_static_colliders or render_mode == 1:
		return

지원 shape 은 Sphere · Box · Capsule · Cylinder · ConcavePolygon · ConvexPolygon · HeightMap · SeparationRay.

⚠️ 공식 위키는 이 기능을 모른다. 위키에는 "Multimesh mode … colliders are ignored" 라고 적혀 있지만 keep_static_colliders 가 생기기 전에 쓰인 문서다. 소스가 정본이다. (최신 커밋 2026-07-26 "fix compatibility issue with 4.7" · plugin.cfg 4.2.0)

설치와 노드 구성

[설치]
방법 A  에디터 ▸ AssetLib 탭 ▸ "ProtonScatter" 검색 ▸ Download ▸ Install
방법 B  저장소를 받아 addons/proton_scatter/ 에 직접 넣는다 (버전 고정 벤더링)
그다음  Project ▸ Project Settings ▸ Plugins ▸ ProtonScatter 를 Enable
ProtonScatter ← 여기에 Keep Static Colliders 가 있다 ├── ScatterItem 무엇을 뿌릴 것인가 │ Source = From disk │ path = res://…/tree.tscn 🛑 프로퍼티 이름은 path 다 └── ScatterShape 어디에 뿌릴 것인가 Shape = ProtonScatterBoxShape
인스펙터에서 켤 것
ProtonScatterRender ModeUse Instancing (= MultiMesh)
ProtonScatterKeep Static Colliders✅ 체크 ← 이 한 번이 전부다

Modifier Stack — 에디터는 자동으로 채운다

ProtonScatter 는 "몇 개를 어디에 놓을지" 를 Modifier Stack 에서 정한다. 에디터에서 노드를 추가하고 인스펙터를 열면 기본 프리셋 4개가 자동으로 들어온다 (stack_panel.gd:73-76). 🛑 코드로 ProtonScatter.new() 하면 비어 있어 인스턴스가 0개가 된다.

Create Inside (Random)   amount = 75
  ▸ Randomize Transforms     rotation (20.0, 360.0, 20.0)
  ▸ Relax Position           iterations 3 · offset_step 0.2
  ▸ Project On Colliders     ray_length 5.0 · collision_mask 1

뿌리고 → 흩고 → 겹침을 풀고 → 지면에 붙이는 순서다.

🛑 반드시 알아야 할 제약 넷 — 전부 실측으로 확인했다

① 씬 트리에 콜리전 노드가 생기지 않는다

PhysicsServer3D 에 직접 등록하므로 Debug ▸ Visible Collision Shapes 를 켜도 안 보인다. 소스 주석에 그렇게 적혀 있다. 확인 방법은 실제로 걸어가서 부딪혀 보는 것뿐이다.

② 부모 노드의 scale 이 버려진다

ProtonScatter 는 원본 씬에서 MeshInstance3DCollisionShape3D 를 재귀로 찾아 그 노드 자신의 transform 만 살린다. 그 사이 부모 노드의 transform 은 전부 버려진다. 루트도 초기화된다.

🛑 이렇게 하면 안 된다

MMTree
└── Body (scale = 5)  ← 버려진다
     └── MeshInstance3D

✅ 이렇게 한다

MMTree
├── Visual (MeshInstance3D)  ← 크기를 메시에
└── StaticBody3D
     └── CollisionShape3D

Source Scale Multiplier 로 키우면 콜리전이 따라오지 않는다

godot --headless --path . -s res://tests/protonscatter_collision_probe.gd
[A. keep_static_colliders = false]            레이 12발 중  0발 명중  ✅ 콜리전 없음
[B. keep_static_colliders = true]             레이 12발 중 12발 명중  ✅ 콜리전 있음
   콜리전 윗면 높이 ≈ 2.27 m
[C. B + source_scale_multiplier = 4]          레이 12발 중  0발 명중  ✅ 실측된 한계
실패 0 건

크기는 원본 씬에서 정한다. Source Scale Multiplier 는 보기에만 쓴다.

④ collision layer 를 설정하지 않는다

소스 전체에 body_set_collision_layer 호출이 0회다. 전부 기본 레이어로 들어간다. 레이어로 구분하는 프로젝트라면 이것이 곧바로 문제가 된다 — 예를 들어 클릭 레이캐스트가 마스크를 잠그지 않았다면 나무를 클릭했을 때 나무 표면이 목표가 된다.

⑦ GridMap + MeshLibrary — 애드온 없이

내장만으로 "노드 1개 + 콜리전 자동" 을 얻는 유일한 경로다. 핵심은 MeshLibrary 아이템이 shape 을 들고 있는 것 하나뿐이다. 엔진 소스(grid_map.cpp _octant_update())가 같은 함수 안에서 multimesh_create() 로 배칭하면서 body_add_shape(g.static_body, …) 로 콜리전을 만든다.

[준비 씬] 아이템마다 이 형식으로 만든다
  Tree (MeshInstance3D)        ← 이 노드 이름이 아이템 이름이 된다
    └ StaticBody3D             ← Mesh ▸ Create Collision Shape ▸ Static Body Child
        └ CollisionShape3D

[내보내기]  Scene ▸ Export As... ▸ MeshLibrary...   →  tree_library.tres

[쓰기]     GridMap 추가 ▸ Inspector ▸ Mesh Library 지정
           ▸ 아래 팔레트에서 아이템을 고르고 뷰포트를 클릭해 배치

🛑 이 형식만 인식된다. 공식 문서 원문 — 아이템은 MeshInstance3D 여야 하고 "Have up to one StaticBody3D child, for collision. The StaticBody3D should have one or more CollisionShape3D children." + "Only this specific format is recognized."

코드로 만들 때는 이 한 줄이 전부다:

var lib := MeshLibrary.new()
lib.create_item(0)
lib.set_item_name(0, "tree")
lib.set_item_mesh(0, tree_mesh)
# [shape, transform] 순서로 넣는다 — 이것이 GridMap 콜리전의 전부다
lib.set_item_shapes(0, [capsule_shape, Transform3D(Basis(), Vector3(0, 1.6, 0))])
✅ 얻는 것🛑 잃는 것
콜리전 자동 · octant 단위 배칭 · 노드 1개 · 내비메시도 가능 격자에만 놓인다 · 회전은 직교 24방향만 · 인스턴스별 크기 무작위 불가

판단 기준 하나 — "격자에 놓여도 어색하지 않은가?" 건물·바닥·벽·울타리·가로등은 ✅. 자연스러운 숲은 ❌.

⑧ 🛑 세 가지 함정

함정무슨 일이 일어나나어떻게
컬링이 전부-아니면-전무 맵 전체를 MultiMesh 1개로 묶으면, 화면에 100그루만 보여도 5,000그루 전부를 GPU 로 보낸다 청크마다 MultiMesh 를 나눈다
LOD 가 전부 같은 단계 거리와 무관하게 인스턴스 전부가 같은 LOD 로 그려진다 청크 분할로 완화된다
삼각형은 줄지 않는다 MultiMesh 는 드로우콜을 줄이는 것이지 삼각형을 줄이지 않는다 삼각형은 LOD·임포스터로

공식 문서 원문 — "there is no screen or frustum culling possible for individual instances. This means, that millions of objects will be always or never drawn"

⑨ 직접 해 본다 — 실습 씬 넷

저장소의 scenes/demo/multimesh/ 에 실습 씬이 있다. 에디터에서 열어 F5 로 실행한다. 방향키로 이동, 마우스 휠로 줌. 주황색 캡슐이 사람이고, 화면 왼쪽 위에 실시간 드로우콜·삼각형·FPS 가 뜬다.

순서무엇을 보나
01_basics.tscn왼쪽 MeshInstance3D 300개 vs 오른쪽 MultiMeshInstance3D 1개 — 드로우콜 차이
02_populate.tscnPopulate Surface 를 직접 눌러 본다. 재료만 들어 있다
03_collision.tscn🛑 핵심. 왼쪽 MultiMesh(통과) vs 오른쪽 ProtonScatter(막힘)
04_gridmap.tscnGridMap + MeshLibrary — 애드온 없이 콜리전 자동

🛑 드로우콜은 F5 로 실행해서 봐야 한다. 에디터 뷰포트의 숫자에는 기즈모·그리드가 섞인다.

③ 이 주장하는 것은 실측으로 확인했다

사람이 걸어가 보기 전에 캐릭터와 똑같은 캡슐로 숲을 가로지르는 직선을 훑었다:

[왼쪽 MultiMesh]       숲 중심까지 4.5 m 를 훑었으나 걸리는 것이 없었다   ✅ 통과
[오른쪽 ProtonScatter]  출발 0.2 m 지점에서 막혔다                       ✅ 막힘
실패 0 건

이어서 읽기

이 페이지는 요약이다. 표·실측·코드 함정은 문서 쪽이 더 자세하다.