Godot 4.4.1 RayCast2D 実践ガイド
レイキャストは現代ゲーム開発において不可欠の技術です。GodotのRayCast2Dノードを使いこなせば、敵の探知、照準システム、地形検出など多彩な機能を実装できます。この記事では、Godot 4.4.1におけるRayCast2Dの基本から実践的な活用例まで解説します。
RayCast2Dとは
RayCast2Dは指定した方向にレイ(光線)を飛ばし、何かに衝突したかを検出するノードです。障害物の検知、視線の通り具合の確認、射撃の命中判定など、様々な用途に使えます。
主な特徴
- 指定方向への直線的な衝突検出
- コリジョンレイヤーによるフィルタリング
- 衝突点や衝突オブジェクトの情報取得
- 処理が軽量で高速
基本的な使い方
RayCast2Dノードの追加
まず、シーンにRayCast2Dノードを追加します。
- シーンタブで親ノードを選択
- 「+」ボタンをクリックまたはCtrl+A
- 検索欄に「RayCast2D」と入力
- RayCast2Dを選択して追加
主要プロパティの設定
インスペクタでこれらの重要なプロパティを設定します:
- Enabled: レイキャストの有効/無効(デフォルトは有効)
- Target Position: レイの方向と長さ(ローカル座標系)
- Collision Mask: 衝突を検出するレイヤー
- Exclude Parent: 親ノードとの衝突を無視するか(デフォルトはtrue)
# コードでプロパティを設定する例
$RayCast2D.enabled = true
$RayCast2D.target_position = Vector2(100, 0) # 右方向に100ピクセル
$RayCast2D.collision_mask = 1 # 1番目のコリジョンレイヤーのみ
衝突検出の基本
RayCast2Dの主要なメソッドを使った衝突検出:
func _physics_process(delta):
# レイの方向を更新(例:マウス方向)
$RayCast2D.target_position = get_local_mouse_position().normalized() * 100
# 衝突検出
if $RayCast2D.is_colliding():
# 衝突点を取得
var collision_point = $RayCast2D.get_collision_point()
# 衝突したオブジェクトを取得
var collider = $RayCast2D.get_collider()
# 衝突点の法線(面の向き)
var normal = $RayCast2D.get_collision_normal()
print("衝突検出: ", collider.name)
print("衝突点: ", collision_point)
print("法線: ", normal)
実践的な活用例
例1: プレイヤーの視界と壁の検出
壁を通り抜けない視界システムの実装:
extends CharacterBody2D
@onready var ray_cast = $RayCast2D
@onready var vision_area = $VisionArea
func _physics_process(delta):
# プレイヤーの向きに応じてレイの方向を更新
ray_cast.target_position = Vector2(cos(rotation), sin(rotation)) * 200
# 壁との衝突をチェック
if ray_cast.is_colliding():
# 壁までの距離を計算
var distance = global_position.distance_to(ray_cast.get_collision_point())
# 視界の範囲を壁までに制限
vision_area.scale = Vector2(distance / 200, distance / 200)
else:
# 壁がなければ最大視界
vision_area.scale = Vector2(1, 1)
例2: 射撃システム
シンプルな射撃システムの実装:
extends Node2D
@onready var ray_cast = $RayCast2D
@onready var gun_sprite = $GunSprite
@onready var muzzle_position = $MuzzlePosition
func _process(delta):
# 銃をマウス方向に向ける
gun_sprite.look_at(get_global_mouse_position())
# レイの方向も更新
ray_cast.global_position = muzzle_position.global_position
ray_cast.target_position = get_local_mouse_position().normalized() * 1000
func _input(event):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
shoot()
func shoot():
if ray_cast.is_colliding():
var target = ray_cast.get_collider()
# 敵に当たったか確認
if target.is_in_group("enemies"):
target.take_damage(10)
# ヒットエフェクト
var hit_effect = preload("res://hit_effect.tscn").instantiate()
get_tree().root.add_child(hit_effect)
hit_effect.global_position = ray_cast.get_collision_point()
例3: 地形傾斜の検出
プレイヤーキャラクターが立っている地面の傾斜を検出:
extends CharacterBody2D
@onready var floor_ray = $FloorRayCast2D
func _physics_process(delta):
# 重力と移動の処理...
# 地面との接触を確認
if is_on_floor():
# 地面の傾斜角度を計算
var floor_normal = floor_ray.get_collision_normal()
var slope_angle = rad_to_deg(acos(floor_normal.dot(Vector2.UP)))
print("地面の傾斜: ", slope_angle, "度")
# 傾斜に応じた処理(例:急な坂では減速)
if slope_angle > 20:
velocity.x *= 0.8
高度なテクニック
複数のレイキャストを使用したセンサー
プレイヤー周囲の状況を把握するためのセンサーシステム:
extends CharacterBody2D
var rays = []
var ray_count = 8
var ray_length = 100
func _ready():
# 周囲に8方向のレイを配置
for i in range(ray_count):
var ray = RayCast2D.new()
add_child(ray)
ray.enabled = true
# レイの角度を計算(円周上に均等配置)
var angle = i * 2 * PI / ray_count
ray.target_position = Vector2(cos(angle), sin(angle)) * ray_length
rays.append(ray)
func _physics_process(delta):
# 各レイの状態をチェック
for i in range(ray_count):
if rays[i].is_colliding():
var obj = rays[i].get_collider()
var dist = global_position.distance_to(rays[i].get_collision_point())
# 方向に応じた処理
if obj.is_in_group("dangers"):
# その方向に障害物があれば回避行動
avoid_danger(i, dist)
レイキャストの結果の可視化
デバッグ用にレイキャストの結果を描画:
extends Node2D
@onready var ray_cast = $RayCast2D
func _draw():
if ray_cast.enabled:
var color = Color.GREEN
var target = ray_cast.target_position
# レイが何かに当たっていれば赤色に
if ray_cast.is_colliding():
color = Color.RED
# ローカル座標系に変換
var collision_point = to_local(ray_cast.get_collision_point())
target = collision_point
# レイを描画
draw_line(Vector2.ZERO, target, color, 2)
# 衝突点に円を描画
if ray_cast.is_colliding():
draw_circle(target, 5, Color.YELLOW)
func _process(delta):
# 描画を更新
queue_redraw()
よくあるエラーと解決策
レイキャストが機能しない場合
-
Enabled プロパティの確認
enabled = trueになっているか確認する
-
コリジョンマスクの設定
- 対象オブジェクトのコリジョンレイヤーがレイキャストのマスクに含まれているか確認
-
ターゲットポジションの設定
- 長さが0になっていないか(
target_position = Vector2.ZERO)
- 長さが0になっていないか(
-
物理エンジンのタイミング
- レイキャストの結果は
_physics_process()で確認する
- レイキャストの結果は
パフォーマンスの問題
- 多数のレイキャストを使用する場合は、本当に必要なものだけ有効にする
- 更新頻度を下げる(毎フレーム更新が必要ない場合)
- レイの長さを必要最小限にする
# パフォーマンス最適化の例
var update_interval = 0.1 # 0.1秒ごとに更新
var timer = 0
func _physics_process(delta):
timer += delta
if timer >= update_interval:
update_raycasts()
timer = 0
func update_raycasts():
# 重要な処理だけを実行
# ...
ベストプラクティス
-
目的に応じたレイの長さ設定
- 必要以上に長いレイは避け、目的に合った長さに設定
-
適切なコリジョンマスク
- 検出したいオブジェクトのレイヤーのみをマスクに含める
-
ローカル・グローバル座標の理解
target_positionはローカル座標であることに注意- グローバル座標での操作が必要な場合は変換を行う
-
デバッグ表示の活用
- 開発中はレイを可視化して動作を確認する
サンプルプロジェクト:敵探知システム
敵を探知してプレイヤーを追跡するAIの例:
extends CharacterBody2D
@export var detection_range = 300
@export var fov_angle = 60 # 視野角(度)
@export var speed = 100
@onready var ray_cast = $RayCast2D
@onready var player = get_tree().get_nodes_in_group("player")[0]
var detected = false
func _physics_process(delta):
if player:
# プレイヤーへの方向ベクトル
var to_player = player.global_position - global_position
var distance = to_player.length()
# 探知範囲内かつ視野角内にいるか確認
if distance < detection_range:
var angle_to_player = rad_to_deg(to_player.angle())
var my_angle = rad_to_deg(rotation)
var angle_diff = abs(angle_to_player - my_angle) % 360
if angle_diff > 180:
angle_diff = 360 - angle_diff
if angle_diff < fov_angle / 2:
# レイキャストでプレイヤーまでの視線が通るか確認
ray_cast.target_position = to_player
ray_cast.force_raycast_update()
if ray_cast.is_colliding() and ray_cast.get_collider() == player:
detected = true
# プレイヤー発見!追跡開始
velocity = to_player.normalized() * speed
else:
detected = false
# 視界が遮られている
velocity = Vector2.ZERO
else:
detected = false
# 視野角外
velocity = Vector2.ZERO
else:
detected = false
# 探知範囲外
velocity = Vector2.ZERO
# 状態に応じた表示の更新
$DetectionIndicator.modulate = Color.RED if detected else Color.GREEN
# 移動を適用
move_and_slide()
まとめ
Godot 4.4.1のRayCast2Dノードは、2Dゲーム開発における強力なツールです。本記事で解説した基本から応用までの技術を活用すれば、以下のようなさまざまな機能を実装できます:
- 敵の視界システム
- 射撃と命中判定
- 地形検出と地形に応じた動作
- 障害物回避
- 複雑なセンサーシステム
効率的なレイキャストの使用は、よりリアルでインタラクティブなゲームプレイを実現する鍵となります。ぜひ自分のプロジェクトに取り入れてみてください。
参考資料
本記事はGodot 4.4.1を基準としています。将来のバージョンでは一部の機能や使い方が変更される可能性があります。