Godot入門:ShapeCast3Dの使い方と活用法

はじめに

RayCast3Dは点の判定だけですが、広い範囲を一度に検出したいことがありませんか?爆発の範囲判定、キャラクターの前方をブロックチェック、敵集団の検出など。ShapeCast3Dなら、球や円柱などの形状を使って、より現実的で広範囲な判定が可能です。

ShapeCast3Dとは?

ShapeCast3Dは、Shape3D(球・カプセル・ボックスなど)を掃引(スイープ)させることで、広い範囲の衝突を検出するノードです。RayCast3D(点の判定)に対して、ShapeCast3Dは形状を持つため、より複雑で現実的な判定が可能になります。複数ヒット検出にも対応しています。

継承ツリー:

ShapeCast3D
  ↓
Node3D
  ↓
Node




ShapeCast3Dの形状判定
ShapeCast3Dはノード位置からtarget_positionへ向かいながら、接触したすべてのオブジェクトを検出します

このノードを使うべき場面

使うべき場面:

  • 爆発やAoE(範囲攻撃)の被弾判定。一度に複数敵を検出
  • キャラクター前方の広域チェック(障害物群の検出)
  • 敵グループが一定範囲内にいるかの判定
  • 射撃ゲームの貫通弾が複数の敵を通る判定
  • 地震やノックバック効果で範囲内のオブジェクトに影響を与えたい場合

使わない場面:

  • 単一ターゲットへの正確な判定(RayCast3Dが効率的)
  • 常時衝突判定(Area3Dを使用)

主なプロパティと機能

プロパティ 説明 使用例
shape Shape3D 掃引に使用する形状(SphereShape3D、CapsuleShape3D、BoxShape3D等) var sphere = SphereShape3D.new(); sphere.radius = 5.0; shapecast.shape = sphere
target_position Vector3 形状の終点(ローカル座標)。ノード位置から終点までが掃引パス shapecast.target_position = Vector3(0, 0, -50)
enabled bool ShapeCast3Dの判定を有効にするか shapecast.enabled = true
collision_mask int どのフィジクスレイヤーを検出するか shapecast.collision_mask = 0b0011
max_results int 最大何個のオブジェクトを検出するか(0で無制限) shapecast.max_results = 10
exclude_parent bool 親ノードを検出対象から除外するか shapecast.exclude_parent = true

重要メソッド

メソッド 戻り値 説明 使用例
is_colliding() bool 何かに衝突しているか if shapecast.is_colliding():
get_collision_count() int 衝突したオブジェクトの数 var count = shapecast.get_collision_count()
get_collider(index: int) Node 指定インデックスの衝突したノード var obj = shapecast.get_collider(0)
get_collision_point(index: int) Vector3 指定インデックスの衝突地点(世界座標) var point = shapecast.get_collision_point(0)
get_collision_normal(index: int) Vector3 指定インデックスの衝突面の法線 var normal = shapecast.get_collision_normal(0)
force_shapecast_update() void 直ちに判定を更新 shapecast.force_shapecast_update()

GDScriptコード例1:爆発範囲判定の実装


extends Node3D

@onready var explosion_check = $ExplosionArea  # ShapeCast3Dを配置

@export var explosion_radius = 20.0
@export var explosion_damage = 50.0

func _ready():
    # 爆発判定用の球形Shapeを設定
    var sphere = SphereShape3D.new()
    sphere.radius = explosion_radius
    explosion_check.shape = sphere

    # 爆発位置から周囲をチェック(掃引なし)
    explosion_check.target_position = Vector3.ZERO

    explosion_check.enabled = true
    explosion_check.collision_mask = 0b0010  # レイヤー1を検出

func trigger_explosion(position: Vector3):
    print("爆発!位置: %s" % position)
    global_position = position

    # 範囲内の全オブジェクトを検出
    explosion_check.force_shapecast_update()

    if explosion_check.is_colliding():
        var hit_count = explosion_check.get_collision_count()
        print("爆発で %d個のオブジェクトに命中" % hit_count)

        for i in range(hit_count):
            var collider = explosion_check.get_collider(i)
            var collision_point = explosion_check.get_collision_point(i)

            print("命中オブジェクト: %s" % collider.name)

            # ダメージ処理
            if collider.has_method("take_damage"):
                collider.take_damage(explosion_damage)

            # ノックバック処理
            if collider.has_method("apply_knockback"):
                var knockback_dir = (collision_point - position).normalized()
                collider.apply_knockback(knockback_dir * 30.0)

GDScriptコード例2:前方チェック(複数障害物検出)


extends CharacterBody3D

@onready var forward_check = $ForwardCheck  # ShapeCast3Dを配置

@export var check_distance = 10.0
@export var check_radius = 2.0

func _ready():
    # カプセル形状で前方の広域チェック
    var capsule = CapsuleShape3D.new()
    capsule.radius = check_radius
    capsule.height = check_radius * 2
    forward_check.shape = capsule

    # ノード位置から前方check_distanceまで掃引
    forward_check.target_position = Vector3(0, 0, -check_distance)
    forward_check.enabled = true
    forward_check.collision_mask = 0b0011

func _physics_process(delta):
    var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")

    if input_dir.length() > 0:
        # 移動方向を更新
        var direction = input_dir.normalized()
        var angle = atan2(direction.x, -direction.y)
        rotation.y = angle

        # 前方の障害物をチェック
        forward_check.force_shapecast_update()

        if forward_check.is_colliding():
            var hit_count = forward_check.get_collision_count()
            print("前方に %d個の障害物を検出" % hit_count)

            for i in range(hit_count):
                var obstacle = forward_check.get_collider(i)
                print("- %s" % obstacle.name)
        else:
            print("前方クリア。進みます")

もっと使いこなす:カスタマイズできるパラメータ

まずは基本を動かしてみてから、余裕が出たら試してみてください。

パラメータ 説明 実装例
collision_group int 自身が属するフィジクスグループ shapecast.collision_group = 0b0001
max_results int 検出上限(0は無制限。大量検出時はパフォーマンス注意) shapecast.max_results = 5 # 最大5個まで
hit_from_inside bool 形状の内側からの衝突も検出 shapecast.hit_from_inside = true
get_collision_shape(index) Shape3D 指定インデックスの衝突形状を取得 var shape = shapecast.get_collision_shape(0)

RayCast3D vs ShapeCast3D 使い分け表

特性 RayCast3D ShapeCast3D
判定範囲 点(光線) 形状(球・カプセル・ボックス)
複数ヒット検出 最初の1つだけ 複数オブジェクト同時検出
処理速度 高速 やや遅い(形状判定のため)
使用場面 FPS射撃、視線チェック 爆発判定、範囲攻撃
セットアップ target_positionのみ shape + target_position

カスタマイズの実装例:複雑な形状(ボックス型判定)


extends Node3D

@onready var box_cast = $BoxCast  # ShapeCast3D

func _ready():
    # 矩形状の判定範囲(例:レーザービーム)
    var box = BoxShape3D.new()
    box.size = Vector3(4.0, 4.0, 50.0)  # 幅4m, 高さ4m, 長さ50m
    box_cast.shape = box

    box_cast.target_position = Vector3(0, 0, 0)  # 掃引なし、現在地のみ検出
    box_cast.enabled = true

func fire_laser():
    box_cast.force_shapecast_update()

    var affected = []
    if box_cast.is_colliding():
        for i in range(box_cast.get_collision_count()):
            affected.append(box_cast.get_collider(i))

    print("レーザーに巻き込まれたオブジェクト: %d個" % affected.size())
    return affected

まとめ

ShapeCast3Dは、広い範囲を効率的に判定し、複数ターゲットに一度にアクセスできる強力なノードです。

  • RayCast3Dとは違い、形状を持つため爆発や範囲攻撃の判定に最適です
  • SphereShape3D・CapsuleShape3D・BoxShape3Dなど、ゲーム設計に応じて形状を選択できます
  • get_collision_count()で複数ヒットを処理し、より複雑なゲーム機構を実装できます

次回は、大量のメッシュを効率的に描画する「MultiMeshInstance3D」について詳しく解説します。

シリーズ:Godot 4 ノード解説

001~040:各種ノード

041~080:
041: Marker3D | 042: Node3D | 043: Camera3D | 044: Light3D | 045: MeshInstance3D |
046: AnimationPlayer | 047: AnimationTree | 048: Skeleton3D | 049: IKSkeleton3D | 050: Skin |
051: CollisionShape3D | 052: RigidBody3D | 053: CharacterBody3D | 054: StaticBody3D | 055: Area3D |
056: PhysicsDirectSpaceState3D | 057: ShapeCast3D | 058: RayCast3D | 059: Decal | 060: GPUParticles3D |
061: CPUParticles3D | 062: WorldEnvironment | 063: OmniLight3D | 064: SpotLight3D | 065: DirectionalLight3D |
066: ReflectionProbe | 067: LightmapGI | 068: VoxelGI | 069: Occluder3D | 070: VisualInstance3D |
071: BoneAttachment3D | 072: VehicleBody3D | 073: SpringArm3D | 074: RayCast3D | 075: ShapeCast3D |
076: MultiMeshInstance3D | 077: Node | 078: CanvasLayer | 079: Control | 080: Label

この記事はGodot 4.xをもとに執筆しています。

コメント