【Unity初心者向け】Raycast の活用③ | 崖から落ちない敵を作る
これまでの記事では、Raycastの基本的な仕組みや、Raycastを使って地面や壁を判定する方法を確認してきました。
今回は、これまで学習した内容を組み合わせて、ゲームでよく使われる動きを作ってみましょう。
作るのは「壁や崖を見つけると反転する敵」です。
敵が足場の上を自動で移動し、
前に壁があると反転する
この先に地面がなければ反転する
という動きを作ります。
Raycastを2本使うことで、敵自身が周囲の地形を調べながら移動できるようになります。
今回作るもの
まずは完成形を確認しておきましょう。
敵は自動で横方向へ移動します。
①途中に壁があった場合は、壁をRaycastで見つけて反対方向へ移動します。
②足場の端まで来た場合も、この先に地面がないことをRaycastで確認して、崖から落ちる前に反転します。

今回使用するのは、次の2本のRaycastです。
前方向のRaycast → 壁を調べる
下方向のRaycast → 地面を調べる
この2つを組み合わせてみましょう。

オブジェクトの配置
ここでは3つのオブジェクトを用意します。
敵(Enemy)
地面(Ground)
かべ(Wall)
敵のオブジェクト作成
任意の敵の素材を取り込んでHierarchy上に配置します。
サンプルでは下のオブジェクトを配置しました。

Inspectorで次のように指定
名前:Enemy
Rigidbody2D:追加
BoxCollider2D:追加
EditColliderで当たり判定の大きさを調整


地面のオブジェクト配置
Enemyの下方向に、Enemyが移動するための地面を用意します。
Hierarchy上で右クリック、2DObject→Sprite→Square で作成します。
ステージ全部を覆うのではなく、Enemyが落下する範囲を残すように大きさを調整します。

Inspectorで次のように指定
名前:Ground
Layer:Ground という Layer を作ってセット
BoxCollider2D:追加

かべのオブジェクト配置
Enemyの進行方向に壁を立てます。
地面と同じように、Hierarchy上で右クリック、2DObject→Sprite→Square で作成します。

Inspectorで次のように指定
名前:Ground
Layer:Ground という Layer を作ってセット
BoxCollider2D:追加

敵の移動プログラム
まずはRaycastを使わず、敵を横方向へ移動させます。
新しいスクリプトファイル:EnemyMove を作成して、敵のGameObjectに追加します。
最初に、次のように記述します。
public class EnemyMove : MonoBehaviour
{
public float speed = 2f; // 敵の移動速度
int direction = 1; // 移動方向(1: 右, -1: 左)
void Update()
{
//指定した方向に移動
transform.position += Vector3.right * direction * speed * Time.deltaTime;
}
}speed は敵の移動速度です。
そして今回重要になるのが、direction です。
directionには、敵が進む方向を入れておきます。
移動処理では、
Vector3.right * directionとしているため、
direction = 1 なら右に進む
direction = -1 なら左に進む
direction = 0 なら停止する
という動きが実行されます。

壁があったら反転する
壁をRaycastで確認する
敵を移動させることができました。
次は、敵の前に壁があるかRaycastを使って確認します。
まず、Raycastで調べるLayerを指定できるように、
public LayerMask groundLayer; // 地面のレイヤーを指定を追加します。
さらに、Update()の中へ次の処理を追加します。
Vector2 rayDirection = Vector2.right * direction; // Raycastの方向を設定
RaycastHit2D wallHit = Physics2D.Raycast(
transform.position, // Raycastの発射位置
rayDirection, // Raycastの方向(右または左)
1.0f, // Raycastの距離(壁との距離を調整)
groundLayer // 地面のレイヤーを指定
);今回注目したいのは、Vector2型で指定した rayDirection です。
rayDirection は 前回解説した Raycast() の ray が飛ぶ方向に使用しています。
Vector2 rayDirection = Vector2.right * direction;で値を指定しています。
敵が右へ進んでいる、つまり direction = 1の場合、右方向へRaycastが飛びます。
一方敵が左へ進んでいる場合は、左方向へRaycastが飛びます。
つまり、敵が進んでいる方向へRaycastを飛ばしているということです。
壁を見つけて方向転換
Raycastで壁を見つけたら、敵の進行方向を反転させます。
次の処理を追加します。
if (wallHit.collider != null) // Raycastが壁に当たった場合
{
direction *= -1; // 移動方向を反転
//Enemyの向きを反転
transform.localScale = new Vector3(-transform.localScale.x, 1, 1);
}前回確認したように、
wallHit.collider != nullは、Raycastが何かのColliderに当たったという意味です。
今回は groundLayer を指定しているため、Groundレイヤーに設定された壁などにRaycastが当たったときに処理が実行されます。
そして、
direction *= -1;によって進行方向を反転させています。
さらに、以下のコードでEnemyのx軸の大きさを反転させることで、Enemyの向きを進行方向に指定しています。
transform.localScale = new Vector3(-transform.localScale.x, 1, 1);
このままだと崖から落ちてしまう
これで、壁にぶつかる前に敵を反転させることができました。
しかし、まだ問題があります。
敵の前に壁がなければ、そのまま進み続けます。
そのため、足場の端まで来ると、そのまま崖から落ちてしまいます。
崖があったら反転する
壁の場合は「前に何かがあるか」を調べました。
しかし崖の場合は、少し考え方が違います。
崖を見つけるためには「この先に地面があるか」を調べます。
そして、地面がなかったら反転するようにすればよいわけです。

下方向へRaycastを飛ばす
地面を確認するため、今度は下方向へRaycastを飛ばします。
Raycastの方向は、Vector2.down です。
例えば、
RaycastHit2D groundHit = Physics2D.Raycast(
transform.position,
Vector2.down,
1f,
groundLayer
);とすれば、敵から下方向へRaycastを飛ばすことができます。
Raycastが地面に当たっていれば、
groundHit.collider != nullとなり、逆に地面がなければ、
groundHit.collider == nullとなり、これを利用すれば崖を見つけることができそうです。
ただし、敵の中心から真下へRaycastを飛ばすだけでは、崖を見つけるタイミングが遅すぎて、判定したときにはもう崖から落ちているかもしれません。
そこで今回は、敵の少し前の位置から下方向へRaycastを飛ばします。

Raycastの開始位置を前へずらす
まず、敵の少し前にRaycastの開始位置を作ります。
Vector2 groundRayOrigin =
(Vector2)transform.position + Vector2.right * direction;少し長く見えますが、分けて考えると簡単です。
まず、
(Vector2)transform.positionで敵の現在位置を取得しています。
そこへ、
Vector2.right * direction右方向へ1マス進んだ方向を加えています。
敵が右へ進んでいる場合は、右方向へ 1マス進んだ場所。
敵が左へ進んでいる場合は、左方向へ 1マス進んだ場所になります。
directionを使っているため、敵が進んでいる方向に合わせて Raycast の開始位置も移動する わけです。
少し前の位置から地面を確認
上で作成した、
groundRayOriginから下方向へRaycastを飛ばします。
RaycastHit2D groundHit = Physics2D.Raycast(
groundRayOrigin, // Raycastの発射位置(敵の前方)
Vector2.down, // Raycastの方向(下方向)
2f, // Raycastの距離(地面との距離を調整)
groundLayer // 地面のレイヤーを指定
);これで、敵の少し前に地面があるか確認できます。
地面の端まで来ると、Raycast に何にも当たらなくなります。
つまり、
groundHit.collider == nullの状態になります。
これを崖の判定として利用します。
地面がなかったら方向転換
地面がない場合は、
if (groundHit.collider == null)
{
direction *= -1; // 移動方向を反転
//Enemyの向きを反転
transform.localScale = new Vector3(-transform.localScale.x, 1, 1);
}とします。
ここで重要なのは、
== nullを使っているところです。壁判定では、
wallHit.collider != nullでした。
これは、壁があることを調べています。
一方、崖判定では、
groundHit.collider == nullこちらは、地面がないことを調べています。
Raycastは、何かに当たったことだけでなく、何にも当たらなかったことも判定に利用できます。
これもRaycastを使ううえで覚えておきたいポイントです。
壁判定と崖判定を組み合わせよう
ここまでで、
前方の壁
足元の地面
をそれぞれ確認できるようになりました。
最後に、この2つを組み合わせます。
敵を反転させたい条件は「壁がある」、または「地面がない」場合です。
そこで、次のように記述できます。
//壁がある、または地面がない場合
if (wallHit.collider != null || groundHit.collider == null)
{
direction *= -1;
transform.localScale = new Vector3(-transform.localScale.x, 1, 1);
}|| は、またはという意味です。

Debug.DrawRayでRaycastを確認しよう
Raycastはゲーム画面には表示されません。
そのため、Raycastの開始位置や方向が正しいか確認するときは、
Debug.DrawRay()を使うと便利です。
まず、壁を調べるRaycastを表示します。
Debug.DrawRay(
transform.position,
rayDirection * 1.0f,
Color.red
);次に、地面を調べるRaycastを表示します。
Debug.DrawRay(
groundRayOrigin,
Vector2.down * 2f,
Color.green
);これでSceneビューには、
赤色 → 壁を確認するRay
緑色 ↓ 地面を確認するRay
が表示されます。
敵を移動させながらSceneビューを見ると、敵の進行方向に合わせてRaycastの向きや位置も変わることを確認できます。
Raycastを使った処理がうまく動かない場合は、まずDebug.DrawRay()を使って、
Raycastの開始位置
Raycastの方向
Raycastの長さ
を確認してみましょう。

完成したスクリプトを確認しよう
ここまでの処理をまとめると、次のようになります。
public class EnemyMove : MonoBehaviour
{
public float speed = 2f; // 敵の移動速度
int direction = 1; // 移動方向(1: 右, -1: 左)
public LayerMask groundLayer; // 地面のレイヤーを指定
void Update()
{
//指定した方向に移動
transform.position += Vector3.right * direction * speed * Time.deltaTime;
// Raycastの方向を設定(右または左)
Vector2 rayDirection= Vector2.right * direction;
RaycastHit2D wallHit = Physics2D.Raycast(
transform.position, // Raycastの発射位置
rayDirection, // Raycastの方向(右または左)
1.0f, // Raycastの距離(壁との距離を調整)
groundLayer // 地面のレイヤーを指定
);
Vector2 groundRayOrigin =
(Vector2)transform.position + Vector2.right * direction;
RaycastHit2D groundHit = Physics2D.Raycast(
groundRayOrigin, // Raycastの発射位置(敵の前方)
Vector2.down, // Raycastの方向(下方向)
2f, // Raycastの距離(地面との距離を調整)
groundLayer // 地面のレイヤーを指定
);
//壁がある、または地面がない場合
if (wallHit.collider != null || groundHit.collider == null)
{
direction *= -1; // 移動方向を反転
// スプライトの向きを反転
transform.localScale = new Vector3(-transform.localScale.x, 1, 1);
}
// デバッグ用のRayを表示
Debug.DrawRay(transform.position, rayDirection * 1.0f, Color.red);
Debug.DrawRay(groundRayOrigin, Vector2.down * 2f, Color.green);
}
}壁との距離や、地面との距離は作成する作品に応じて調整してみましょう。
今回の処理の流れを整理しよう
今回作った処理は、一見すると少し複雑に見えるかもしれません。
しかし、流れを整理すると非常にシンプルです。
1,まず敵を移動します。
2,そして、前方向へRaycastを飛ばして前方に壁があるか判定。
3,同時に、敵の少し前から下方向へRaycastを飛ばして地面があるか判定。
4,壁がある、または地面がない場合に反転して方向転換。
これだけです。

今回のまとめ
今回は、Raycastを使って崖から落ちない敵を作りました。
今回のポイントは次のとおりです。

次回はプレイヤーが見えているか調べてみよう
今回は、Raycastを使って敵の周囲にある地形を調べました。
次回は少し使い方を変えて、敵からプレイヤーが見えているか をRaycastで調べてみます。
敵とプレイヤーの間に壁がなければ「プレイヤーを発見した」と判断できます。
反対に、途中に壁があれば、プレイヤーは見えていないと判断できます。
Raycastを使った視線判定を作ってみましょう。
こちらはUnity初心者向けの記事をテーマ別にまとめています。
▼ Unity初心者向け記事まとめはこちら
こちらのマガジン「ミニゲーム作成講座 全8回セット」でもシーンの切り替えなどを活用して、初心者でも知識ゼロから作れるミニゲーム作成講座を紹介しています。

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

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