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

はじめに

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

ShapeCast3Dとは?

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

継承ツリー:

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

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

使うべき場面:

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

使わない場面:

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

主なプロパティと機能

プロパティ説明使用例
shapeShape3D掃引に使用する形状(SphereShape3D、CapsuleShape3D、BoxShape3D等)var sphere = SphereShape3D.new(); sphere.radius = 5.0; shapecast.shape = sphere
target_positionVector3形状の終点(ローカル座標)。ノード位置から終点までが掃引パスshapecast.target_position = Vector3(0, 0, -50)
enabledboolShapeCast3Dの判定を有効にするかshapecast.enabled = true
collision_maskintどのフィジクスレイヤーを検出するかshapecast.collision_mask = 0b0011
max_resultsint最大何個のオブジェクトを検出するか(0で無制限)shapecast.max_results = 10
exclude_parentbool親ノードを検出対象から除外するか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_groupint自身が属するフィジクスグループshapecast.collision_group = 0b0001
max_resultsint検出上限(0は無制限。大量検出時はパフォーマンス注意)shapecast.max_results = 5 # 最大5個まで
hit_from_insidebool形状の内側からの衝突も検出shapecast.hit_from_inside = true
get_collision_shape(index)Shape3D指定インデックスの衝突形状を取得var shape = shapecast.get_collision_shape(0)

RayCast3D vs ShapeCast3D 使い分け表

特性RayCast3DShapeCast3D
判定範囲点(光線)形状(球・カプセル・ボックス)
複数ヒット検出最初の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.xをもとに執筆しています。

コメント