見出し画像

【Unity初心者向け】Raycast の活用③ | 崖から落ちない敵を作る

これまでの記事では、Raycastの基本的な仕組みや、Raycastを使って地面や壁を判定する方法を確認してきました。

今回は、これまで学習した内容を組み合わせて、ゲームでよく使われる動きを作ってみましょう。

作るのは「壁や崖を見つけると反転する敵」です。

敵が足場の上を自動で移動し、

  • 前に壁があると反転する

  • この先に地面がなければ反転する

という動きを作ります。
Raycastを2本使うことで、敵自身が周囲の地形を調べながら移動できるようになります。



今回作るもの

まずは完成形を確認しておきましょう。

敵は自動で横方向へ移動します。
①途中に壁があった場合は、壁をRaycastで見つけて反対方向へ移動します。
②足場の端まで来た場合も、この先に地面がないことをRaycastで確認して、崖から落ちる前に反転します。

今回使用するのは、次の2本のRaycastです。

  • 前方向のRaycast → 壁を調べる

  • 下方向のRaycast → 地面を調べる

この2つを組み合わせてみましょう。


オブジェクトの配置

ここでは3つのオブジェクトを用意します。

  1. 敵(Enemy)

  2. 地面(Ground)

  3. かべ(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の個別指導のオンラインレッスンを行っています。
ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。

いいなと思ったら応援しよう!

ファイブボックス よろしければ応援お願いします! いただいたチップはクリエイターとしての活動費に使わせていただきます!