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

はじめに

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

RayCast3Dとは?

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

継承ツリー:

RayCast3D
  ↓
Node3D
  ↓
Node




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

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

使うべき場面:

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

使わない場面:

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

主なプロパティと機能

プロパティ 説明 使用例
enabled bool RayCast3Dの判定を有効にするか raycast.enabled = true
target_position Vector3 ノード相対での光線の終点(ローカル座標) raycast.target_position = Vector3(0, 0, -100)
collision_mask int どのフィジクスレイヤーを検出するか raycast.collision_mask = 0b0011
exclude_parent bool 親ノードを判定から除外するか raycast.exclude_parent = true
hit_from_inside bool 形状の内側からの検出を許可するか 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_group int 自身が属するフィジクスグループ raycast.collision_group = 0b0001
hit_from_inside bool オブジェクトの内側からのレイ衝突も検出 raycast.hit_from_inside = true
get_collision_shape() Shape3D 衝突したコリジョン形状を取得 var shape = raycast.get_collision_shape()
debug_shape_custom_color Color デバッグ表示時のレイの色(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 ノード解説

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をもとに執筆しています。

コメント