はじめに
3Dレベルデザインやプロトタイプ開発で、複雑な形状を素早く作る必要があることがあります。「この部分を切り取りたい」「2つの形を組み合わせたい」——こうした要望を簡単に実現するのがCSGBox3Dです。CSG(Constructive Solid Geometry)という手法でメッシュを組み合わせることで、複雑な3D形状を効率的に設計できます。
本記事では、CSGBox3Dとその仲間たち、そしてCSGの本質を、実装パターンを交えて解説します。
CSGBox3Dとは?
CSGBox3Dは、ブーリアン演算を用いて立方体型の3Dオブジェクトを作成・結合するノードです。他のCSGプリミティブと組み合わせることで、複雑な形状を簡単に設計できます。
継承ツリー:
CSGBox3D → CSGPrimitive3D → CSGShape3D → GeometryInstance3D → VisualInstance3D → Node3D → Node → Object
CSGの「構築的立体幾何」とは、単純な形(立方体、球体、円柱)を組み合わせ、加算・減算・交差といった演算を用いて複雑な形を作る手法です。ゲーム開発では、プロトタイプやレベルデザインの高速化に欠かせません。
このノードを使うべき場面
活躍する場面:
- ゲームプロトタイプの素早い環境構築
- レベルデザインの初期段階(調整が多い場合)
- 建築物やダンジョンの型抜き(ドア・窓の形状)
- 複雑な壁配置や障害物の配置
- ゲームジャムなど時間が限定された開発
使わない場面:
- 本番ゲーム(パフォーマンス最適化後は MeshInstance3D に置き換え)
- 高度にディテールが必要なアセット(モデリングソフトで作成)
- 頻繁に動く複雑な形状(負荷が高い)
主なプロパティと機能
CSGBox3Dの基本的なプロパティを表にまとめました:
| プロパティ | 型 | 説明 | デフォルト値 |
|---|---|---|---|
| size | Vector3 | 立方体のサイズ(X, Y, Z) | Vector3(2, 2, 2) |
| material | Material | 表面のマテリアル | null |
| use_collision | bool | 衝突判定を有効にするか | false |
| collision_layer | int | 所属するコリジョンレイヤー | 1 |
| collision_mask | int | 検出するコリジョンレイヤー | 1 |
| operation | enum | ブーリアン演算の種類 | OPERATION_UNION |
基本的な使用例:
extends Node3D
@onready var csg_box = $CSGBox3D
func _ready():
# シンプルな立方体
csg_box.size = Vector3(2, 2, 2)
csg_box.material = StandardMaterial3D.new()
csg_box.material.albedo_color = Color.GRAY
# 衝突判定を有効化
csg_box.use_collision = true
csg_box.collision_layer = 1
csg_box.collision_mask = 1
print("CSGBox3D が作成されました: サイズ", csg_box.size)

ドア・窓の型抜き実装例:
extends Node3D
func _ready():
# 壁となるCSGBox3D(親)
var wall = CSGBox3D.new()
wall.size = Vector3(10, 5, 1)
wall.material = StandardMaterial3D.new()
wall.material.albedo_color = Color(0.8, 0.7, 0.6) # 土色
wall.use_collision = true
add_child(wall)
# ドアの穴をくり抜く(CSGBox3D)
var door_hole = CSGBox3D.new()
door_hole.size = Vector3(2, 3, 1.5)
door_hole.position = Vector3(3, 1, 0) # 壁の右上に配置
door_hole.operation = CSGShape3D.OPERATION_SUBTRACTION # 減算演算
wall.add_child(door_hole)
# 窓の穴をくり抜く(CSGBox3D)
var window_hole = CSGBox3D.new()
window_hole.size = Vector3(2, 1.5, 1.5)
window_hole.position = Vector3(-3, 3, 0) # 壁の左上に配置
window_hole.operation = CSGShape3D.OPERATION_SUBTRACTION # 減算演算
wall.add_child(window_hole)
print("壁にドアと窓がくり抜かれました")
CSGの仲間たち
CSGBox3D だけでなく、複数のプリミティブが用意されています:
| CSGノード | 形状 | 主要プロパティ | 用途例 |
|---|---|---|---|
| CSGBox3D | 立方体 | size | 壁、床、建築物 |
| CSGSphere3D | 球体 | radius, radial_segments, rings | 爆発範囲、丸い装飾 |
| CSGCylinder3D | 円柱 | radius, height | 柱、パイプ、筒状オブジェクト |
| CSGMesh3D | 任意のメッシュ | mesh | 複雑な形状の加工 |
| CSGCombiner3D | 複合形状の親 | なし(コンテナ) | 複数のCSGを統合 |
CSGCombiner3D を使った複合形状の作り方
複数のCSGノードを組み合わせる場合、CSGCombiner3D を親として使うと整理しやすくなります:
extends Node3D
func _ready():
# CSGCombiner3D(複合体の親)
var combiner = CSGCombiner3D.new()
add_child(combiner)
# 基盤(床)
var base = CSGBox3D.new()
base.size = Vector3(5, 0.5, 5)
base.position = Vector3(0, 0, 0)
base.operation = CSGShape3D.OPERATION_UNION
combiner.add_child(base)
# 塔(中央)
var tower = CSGCylinder3D.new()
tower.radius = 1.0
tower.height = 4.0
tower.position = Vector3(0, 2, 0)
tower.operation = CSGShape3D.OPERATION_UNION
combiner.add_child(tower)
# 球の装飾(塔の頂上)
var decoration = CSGSphere3D.new()
decoration.radius = 0.7
decoration.position = Vector3(0, 4.5, 0)
decoration.operation = CSGShape3D.OPERATION_UNION
combiner.add_child(decoration)
# コアをくり抜く(塔の内部)
var core = CSGCylinder3D.new()
core.radius = 0.6
core.height = 4.5
core.position = Vector3(0, 2, 0)
core.operation = CSGShape3D.OPERATION_SUBTRACTION
combiner.add_child(core)
print("複合形状が作成されました")

もっと使いこなす:カスタマイズできるパラメータ
まずは基本を動かしてみてから、余裕が出たら試してみてください。
| パラメータ | 設定値の例 | 効果 |
|---|---|---|
| smoothing_enabled | true/false | スムーズシェーディング。true で滑らかな見た目 |
| invert_faces | true/false | 面の向きを反転。内側を表示したい場合に使用 |
| material | StandardMaterial3D など | 色・テクスチャなどの外観設定 |
| collision_layer / mask | 1 〜 32 | 複数のレイヤーで衝突判定を細かく制御 |
| operation | UNION / INTERSECTION / SUBTRACTION | 他のCSGとの演算方式を指定 |
本番環境への置き換え:MeshInstance3D + CollisionShape3D
プロトタイプから本番環境への移行では、CSGを MeshInstance3D に置き換えることが推奨されます。理由はパフォーマンス最適化です:
# CSGを使った設計完了後、本番用に置き換える例
# 方法1: CSGから静的メッシュをエクスポート
# Godot Editor で、CSGShape3D を右クリック → "メッシュをエクスポート"
# 方法2: プログラムで置き換え
func convert_csg_to_mesh(csg_node: CSGShape3D):
var mesh_instance = MeshInstance3D.new()
mesh_instance.mesh = csg_node.get_meshes()[1] # CSGのメッシュを取得
mesh_instance.material_override = csg_node.material
var collision_shape = CollisionShape3D.new()
collision_shape.shape = csg_node.get_collision_shape()
return [mesh_instance, collision_shape]

まとめ
CSGBox3D とそのCSGファミリーは、3D環境のプロトタイピングを劇的に高速化するノードです。ブーリアン演算を活用することで、複雑な形状も直感的に設計できます。ただし本番環境ではパフォーマンスのため MeshInstance3D への置き換えが推奨されます。
- OPERATION_UNION(加算)・SUBTRACTION(減算)・INTERSECTION(交差)を使い分けることで、複雑な形状が簡単に作成できる
- use_collision を有効化することで、CSG形状そのものが衝突判定として機能
- CSGCombiner3D で複数のプリミティブを統合し、整理された階層構造を作る
- 開発完了後は、パフォーマンス最適化のため MeshInstance3D への置き換えを検討
次回は GridMap について解説します。タイルベースの3D環境を効率的に構築するノードです。
この記事はGodot 4.xをもとに執筆しています。
シリーズ:Godot 4 ノード解説
001〜040:各種ノード
- 041:Control
- 042:Button
- 043:Label
- 044:TextEdit
- 045:Panel
- 046:VBoxContainer
- 047:AnimationPlayer
- 048:Timer
- 049:Area3D
- 050:RigidBody3D
- 051:CharacterBody3D
- 052:CollisionShape3D
- 053:MeshInstance3D
- 054:Node3D
- 055:Camera3D
- 056:Sprite3D
- 057:DirectionalLight3D
- 058:OmniLight3D
- 059:ReflectionProbe
- 060:VisibleOnScreenNotifier3D
- 061:SpotLight3D
- 062:WorldEnvironment
- 063:AudioStreamPlayer3D
- 064:GPUParticles3D
- 065:CSGBox3D
- 066:GridMap
- 067:Decal
- 068:OccluderInstance3D
- 069:LightmapGI
- 070:SubViewport


コメント