【Unity初心者向け】Raycast の活用④ | 敵からプレイヤーが見えているか判定する
前回は、前方向と下方向へRaycastを飛ばして、壁や崖を見つけて方向転換する敵を作りました。
これまでのRaycastでは、
下方向へ飛ばして地面を確認する
前方向へ飛ばして壁を確認する
というように、決められた方向へRayを飛ばしていました。
今回は、敵からプレイヤーへ向かってRaycastを飛ばし、プレイヤーが見えているか調べます。
例えば、敵とプレイヤーの間に何もなければ、Raycast が Player まで届くので、プレイヤーを発見できます。
一方、途中に壁があると、Raycast は先に壁へ当たります。
この場合は「壁の向こうにPlayerがいるが、Enemyからは見えていない」と判断できます。
この仕組みを使うと、
プレイヤーを発見する
壁の向こうにいれば気づかない
発見したら追いかける
見失ったら動きを止める
といった、敵AIの基本的な仕組みを作ることができます。
今回は、Raycastを敵の視線として使ってみましょう。

ステージの準備
今回は、仕組みを分かりやすくするために、ステージを上から見た形で作ってみます。
必要なGameObjectは、
Enemy
Player
Wall
の3種類、例えば次のように配置します。

EnemyとPlayerの間にWallを移動できるようにしておくと、今回の動きを確認しやすくなります。
Player と Wall には、Raycastが当たるようにCollider 2Dを設定しておきましょう。

Enemyのプログラム
Playerの場所を探す
まず、EnemyからPlayerの位置を取得できるようにします。
スクリプトファイル:EnemySight を作成して、Enemyへアタッチします。
最初は次のように記述します。
public class EnemySight : MonoBehaviour
{
public Transform player;//Playerの位置を取得
}今回はPlayerの位置を使用したいので、Transform型で用意しています
スクリプトをEnemyへ追加すると、Inspector にPlayerを設定する欄が表示されます。
Hierarchy にあるPlayerをドラッグ&ドロップして設定してください。

player.positionを使ってPlayerの現在位置を取得できるようになります。

Playerがいる方向を求める
ここからが今回の重要なポイントです。
これまで Raycast の方向には、
Vector2.right
Vector2.down
を使ってきました。
しかし今回は、Playerがどこにいるかによって Raycast を飛ばす方向が変わります。
そこで、Enemy から Player へ向かう方向を計算します。
次のように記述します。
void Update()
{
// プレイヤーの方向を計算
Vector2 direction = player.position - transform.position;
}Player の位置から Enemy の位置を引くことで、Enemy から Player へ向かう方向を作ることができます。
例えば、EnemyがX座標:2、PlayerがX座標:7にいる場合は、
7 - 2 = 5
となります。
正の値になるので、Enemyから見てPlayerは右方向にいます。
Playe rが Enemy より左側へ移動すると結果は負の値になり、今度は左方向になります。
つまり、Playerがどこへ移動しても、
player.position - transform.positionによって Enemy から Player へ向かう方向を求めることができます。

normalizedで方向の長さをそろえる
先ほど作った direction には、方向だけでなくEnemyとPlayerの距離も含まれています。
そこで .normalized を使います。
コードを次のように変更します。
void Update()
{
// プレイヤーの方向を計算して正規化する
Vector2 direction =
(player.position - transform.position).normalized;
}normalized を使うと、ベクトルの向きはそのままにして、長さを1にできます。
今回は、
direction → どちらへ飛ばすか
viewDistance → どこまで飛ばすか
というように、方向と距離を別々に管理したいのでnormalizedを使います。ここでは、「Playerへ向かう方向だけを取り出している」と考えておけばOKです。

Playerへ向かってRaycastを飛ばす
方向が作れたので、Raycastを飛ばしてみましょう。
まずRaycastの距離を設定するために、
public float viewDistance = 10f;を追加します。
そしてUpdateの中でRaycastを使用します。
public float viewDistance = 5f; // 視界の距離
void Update()
{
// 記述済みコード
Vector2 direction =
(player.position - transform.position).normalized;
// プレイヤーの方向にRaycastを飛ばす
RaycastHit2D hit = Physics2D.Raycast(
transform.position, // Raycastの発射位置
direction, // Raycastの方向(プレイヤーの方向)
viewDistance // Raycastの距離(視界の距離)
);
}これまで使用していた、
Vector2.right
Vector2.down
の代わりに、今回は自分で計算した direction を使っているわけです。
Raycastの可視化
Raycast は、そのままではGameビューやSceneビューに線として表示されません。
そこで今回も、
Debug.DrawRay()を使って確認してみましょう。
Updateの中へ追加します。
// Raycastを可視化
Debug.DrawRay(
transform.position, // Raycastの発射位置
direction * viewDistance, // Raycastの方向と距離を指定
Color.red // Raycastの色を赤に設定
);全体は次のようになります。
ゲームを実行してSceneビューを確認してみましょう。
EnemyからPlayerの方向へ赤い線が伸びます。
Playerの位置を変えると、Rayの方向もPlayerに合わせて変化します。

Raycastが何に当たったか調べる
Raycast を Player へ向けられるようになりました。
次は、Raycast が何に当たったのかを確認します。
これまでにも使用した、hit.collider を使います。
if (hit.collider != null) // Raycastが何かに当たった場合
{
Debug.Log(hit.collider.name);
}これを Update() に追加してゲームを実行してみましょう。
Enemy と Player の間に何もなければ、Raycast は Player に当たります。
この場合、hit.collider には Player の Collider が入ります。
Consoleにも「Player」が表示されます。

では、EnemyとPlayerの間にWallを置いてみましょう。
今度はRaycastが先にWallへ当たるので、hit.collider には Wall の Collider が入ります。
Consoleには「Wall」が表示されます。

Raycast が Player に当たったなら Player が見えている。
Raycast が Wall に当たったなら、その先にいる Player は見えていない。
という判定ができます。

PlayerかどうかをTagで確認
Raycastが何に当たったか確認できたので、今度は当たったGameObjectがPlayerなのかを調べます。
Player の Inspector をから Tag に Player を指定します。

さらにコードを次のように変更します。
if (hit.collider != null)
{
if (hit.collider.CompareTag("Player")) // プレイヤーに当たった場合
{
Debug.Log("プレイヤー発見!");
}
}CompareTag() を使うと、GameObject に設定されている Tag を確認できます。
Player に直接 Raycast が当たれば、
プレイヤー発見!と Console へ表示されます。
途中にWallを置くと、Raycastが当たるのはWallです。
そのため、CompareTag("Player") は false となり「プレイヤー発見!」は表示されません。
これだけでも、PlayerがEnemyから見えているかを判定できるようになりました。
LayerMaskで調べる対象を限定する
現在のRaycastは、途中にあるさまざまなColliderへ反応します。
第2回では、Raycastで調べる対象を限定するために LayerMask を使用しました。
今回も LayerMask を使ってみましょう。
例えば、次の2つの Layer を用意します。
Player
Wall
Playerには、Layer:Player、Wallには、Layer:Wall をそれぞれを設定します。

EnemySight には、
public LayerMask detectionLayer; // プレイヤーを検知するレイヤーを追加します。
Inspector に Detection Layer が表示されるので、Player, Wall の両方を選択します。

そして Raycast に検知対象のレイヤーを追加します。
次のように変更してください。
RaycastHit2D hit = Physics2D.Raycast(
transform.position,
direction,
viewDistance,
detectionLayer // プレイヤーを検知するレイヤーを指定
);これで今回のRaycastは、PlayerとWallのColliderだけを調べるようになります。
PlayerだけをLayerMaskにしてはいけない?
ここで注意があります。
Playerを探しているのだから、
Detection Layer
☑ Player
□ Wallでよさそうに思えるかもしれません。
しかし、これでは今回の目的を正しく実現できません。
例えば、Enemy と Player の間に Wall があった場合を考えてみます。
Raycast の対象が Player だけだと、Wall の Collider は調べる対象から外れます。
すると Raycast は Wall を無視して、その先にいる Player を検出してしまいます。
結果として、壁の向こうにいるのにPlayerを発見してしまうことになります。
今回調べたいのは「Playerがいるか」だけではありません。
EnemyとPlayerの間に「視線を遮るものがあるか」も調べる必要があります。
そのため、
Detection Layer
☑ Player
☑ Wallのように、Playerと視線を遮るWallの両方をRaycastの対象にします。

完成したスクリプトを確認
今回のEnemySightスクリプトは次のようになります。
public class EnemySight : MonoBehaviour
{
public Transform player;//Playerの位置を取得
public float viewDistance = 10f; // 視界の距離
public LayerMask detectionLayer; // プレイヤーを検知するレイヤーを指定
void Update()
{
// プレイヤーの方向を計算して正規化する
Vector2 direction =
(player.position - transform.position).normalized;
// プレイヤーの方向にRaycastを飛ばす
RaycastHit2D hit = Physics2D.Raycast(
transform.position, // Raycastの発射位置
direction, // Raycastの方向(プレイヤーの方向)
viewDistance, // Raycastの距離(視界の距離)
detectionLayer // プレイヤーを検知するレイヤーを指定
);
// Raycastを可視化
Debug.DrawRay(
transform.position, // Raycastの発射位置
direction * viewDistance, // Raycastの方向と距離を指定
Color.red // Raycastの色を赤に設定
);
if (hit.collider != null) // Raycastが何かに当たった場合
{
if (hit.collider.CompareTag("Player")) // プレイヤーに当たった場合
{
Debug.Log("プレイヤー発見!");
}
}
}
}ゲームを実行して確認してみましょう。
EnemyとPlayerの間に何もなければ、Console には、
プレイヤー発見!と表示されます。
間にWallを置くと、Raycast が Wall へ当たるので、Player は発見されません。
Wall を移動して視線が通るようにすると、再び Player を発見します。
これで、敵からPlayerが見えているか をRaycastで判定できました。
発見したあとの処理
今回は分かりやすくするため、
Debug.Log("プレイヤー発見!");と表示するだけにしています。
しかし実際のゲームでは、この部分にさまざまな処理を入れることができます。
例えば、
追いかける
攻撃する
警戒状態へ変更する
といった使い方ができます。
つまりRaycastは、敵がPlayerを発見するきっかけとして利用できます。

今回のまとめ
今回は、Raycastを使ってEnemyからPlayerが見えているか判定する方法を確認しました。
今回のポイントは次のとおりです。

次回は……
今回は、Raycastを敵の視線として使い、Playerが見えているかを判定しました。
Raycastで取得できる情報は、hit.colliderだけではありません。
次回は、hit.point や、hit.normal など、Raycastが当たった位置や面の向きにも注目してみます。
Raycastで取得できる情報をさらに詳しく確認しながら、シリーズの内容をまとめていきましょう。
こちらはUnity初心者向けの記事をテーマ別にまとめています。
▼ Unity初心者向け記事まとめはこちら
こちらのマガジン「ミニゲーム作成講座 全8回セット」でもシーンの切り替えなどを活用して、初心者でも知識ゼロから作れるミニゲーム作成講座を紹介しています。

さらにステップアップの「スマホで遊ぶジャンプゲーム 8回セット」のマガジンもぜひご活用ください。

ファイブボックスでは、スクラッチやUnityの個別指導のオンラインレッスンを行っています。
ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。
いいなと思ったら応援しよう!
よろしければ応援お願いします! いただいたチップはクリエイターとしての活動費に使わせていただきます!