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

はじめに

FPS視点で銃を撃つとき、照準が何を指しているかを判定したいですよね。または、キャラクターが立っている地面を検出したい、壁が目の前にあるかチェックしたい、そんな場面で活躍するのがRayCast3Dです。このノードは特定方向に光線を飛ばして、最初に当たったオブジェクトを瞬時に検出します。

RayCast3Dとは?

RayCast3Dは、特定方向に光線(レイ)を飛ばして、最初に当たったオブジェクトを検出するノードです。2DゲームのRayCast2Dの3D版で、视线判定・射撃・地面感知など、多くのゲーム機構に必要な「何が見えているか」「何に当たるか」を調べます。

継承ツリー:

RayCast3Dの光線判定
RayCast3Dはノード位置からtarget_positionまでの直線上で、最初に衝突したオブジェクトを検出します

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

使うべき場面:

  • FPS・TPSゲームの銃砲撃撃時の判定(何に当たったか)
  • キャラクターの足元が地面に着いているかを検出(落下判定)
  • 壁や障害物がキャラクターの目の前にあるか確認(視線チェック)
  • レーダーやスキャナーの敵検出機能
  • インタラクション可能なオブジェクトが視野内にあるか判定(ピックアップアイテム検出)

使わない場面:

  • 広い範囲の複数オブジェクト検出(ShapeCast3Dやconvex_shape_queryを使用)
  • 物理ボディの常時衝突判定(PhysicsDirectSpaceState3Dのクエリ系メソッド)

主なプロパティと機能

プロパティ説明使用例
enabledboolRayCast3Dの判定を有効にするかraycast.enabled = true
target_positionVector3ノード相対での光線の終点(ローカル座標)raycast.target_position = Vector3(0, 0, -100)
collision_maskintどのフィジクスレイヤーを検出するかraycast.collision_mask = 0b0011
exclude_parentbool親ノードを判定から除外するかraycast.exclude_parent = true
hit_from_insidebool形状の内側からの検出を許可するかraycast.hit_from_inside = false

重要メソッド

メソッド戻り値説明使用例
is_colliding()bool何かに衝突しているかif raycast.is_colliding():
get_collider()Node衝突したノードvar obj = raycast.get_collider()
get_collision_point()Vector3衝突した世界座標var point = raycast.get_collision_point()
get_collision_normal()Vector3衝突面の法線ベクトルvar normal = raycast.get_collision_normal()
force_raycast_update()void直ちに判定を更新(同フレーム内での判定を即座に取得)raycast.force_raycast_update()

GDScriptコード例1:FPS射撃の実装


extends Node3D

@onready var raycast = $Camera3D/RayCast3D

@export var damage = 10.0
@export var fire_distance = 1000.0

func _ready():
    # RayCast3Dを画面中央から前方へ向ける
    raycast.target_position = Vector3(0, 0, -fire_distance)
    raycast.enabled = true

func _process(delta):
    if Input.is_action_just_pressed("ui_accept"):  # スペースキー射撃
        shoot()

func shoot():
    print("射撃!")

    # 照準が敵に当たっているか確認
    if raycast.is_colliding():
        var collider = raycast.get_collider()
        var collision_point = raycast.get_collision_point()

        print("敵に命中: %s" % collider.name)
        print("命中地点: %s" % collision_point)

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

        # ヒットエフェクト表示
        create_impact_effect(collision_point)
    else:
        print("空に射撃")

func create_impact_effect(position: Vector3):
    # ヒットエフェクトを表示(パーティクルなど)
    pass

GDScriptコード例2:地面検出と落下判定


extends CharacterBody3D

@onready var ground_check = $GroundCheck  # RayCast3Dを子ノードとして配置

@export var move_speed = 7.0
@export var jump_force = 20.0
@export var gravity = 30.0

var is_on_ground = false

func _ready():
    # 足元を検出するRayCast3Dを設定
    ground_check.target_position = Vector3(0, -0.1, 0)  # 足元0.1m下
    ground_check.enabled = true

func _physics_process(delta):
    # 地面接触判定
    ground_check.force_raycast_update()
    is_on_ground = ground_check.is_colliding()

    # キャラクター移動
    var input_dir = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    var direction = transform.basis * Vector3(input_dir.x, 0, input_dir.y)

    velocity.x = direction.x * move_speed
    velocity.z = direction.z * move_speed

    # ジャンプ
    if Input.is_action_just_pressed("ui_space") and is_on_ground:
        velocity.y = jump_force
        print("ジャンプ!")

    # 重力
    if not is_on_ground:
        velocity.y -= gravity * delta

    velocity = move_and_slide(velocity)

    # デバッグ表示
    if is_on_ground:
        var ground_object = ground_check.get_collider()
        print("接地: %s" % ground_object.name)
    else:
        print("落下中")

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

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

パラメータ説明実装例
collision_groupint自身が属するフィジクスグループraycast.collision_group = 0b0001
hit_from_insideboolオブジェクトの内側からのレイ衝突も検出raycast.hit_from_inside = true
get_collision_shape()Shape3D衝突したコリジョン形状を取得var shape = raycast.get_collision_shape()
debug_shape_custom_colorColorデバッグ表示時のレイの色(Debugモード)raycast.debug_shape_custom_color = Color.RED

カスタマイズの実装例:複数方向の視野チェック


extends Node3D

var view_raycasts = []

func _ready():
    # 前方左45度、前方、前方右45度の3方向を監視
    var angles = [-PI/4, 0, PI/4]

    for angle in angles:
        var rc = RayCast3D.new()
        rc.target_position = Vector3(sin(angle) * 100, 0, -cos(angle) * 100)
        rc.enabled = true
        rc.collision_mask = 0b0010  # レイヤー1を検出
        add_child(rc)
        view_raycasts.append(rc)

func check_forward_vision():
    var enemies_detected = []

    for rc in view_raycasts:
        rc.force_raycast_update()
        if rc.is_colliding():
            var collider = rc.get_collider()
            if "Enemy" in collider.name:
                enemies_detected.append(collider)

    if enemies_detected.size() > 0:
        print("敵を検出: %d体" % enemies_detected.size())
    else:
        print("敵なし")

    return enemies_detected

まとめ

RayCast3Dは、ゲーム内での「何が見えているか」「何に当たるか」を判定する最もシンプルで高速なノードです。

  • target_positionで光線の方向と距離を指定し、is_colliding()で衝突判定、get_collider()で衝突対象を取得できます
  • FPS射撃、地面検出、視線チェックなど、様々なゲーム機構に応用できます
  • 複数のRayCast3Dを組み合わせることで、複雑な視野判定や広域スキャンを実装できます

次回は、広い範囲を一度に判定できる「ShapeCast3D」について詳しく解説します。

関連性の高いノード解説記事

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

コメント