見出し画像

【Unity初心者向け】Raycast の活用⑤ | Raycastで攻撃|hit.point・hit.normalを使ってみよう

これまでRaycastを使って、

  • 地面や壁を調べる

  • 崖から落ちない敵を作る

  • EnemyからPlayerが見えているか調べる

といった処理を確認してきました。
Raycastは、周囲の状況を調べるだけではありません。
ゲームでは、攻撃の当たり判定にも利用できます。

今回はRaycastシリーズの最終回として、Raycastを使った攻撃を作ってみましょう。

さらに今回は、hit.point と、hit.normal についても確認します。
Raycastがどこに当たったのか、そして、当たった面がどちらを向いているのかまで調べられるようになります。


Raycastを使った攻撃とは

ゲームで銃を撃つ場合、実際に弾のGameObjectを作って飛ばす方法があります。

例えば、Instantiate() 関数を使って弾を生成し、その弾を移動させる方法もよく使われます。

しかし、Raycast を使って攻撃を行う方法もあります。
具体的には Raycast を飛ばして、その先にEnemyがいるかを調べます。

この場合、実際に弾のGameObjectを飛ばす必要はありません。
Raycastを飛ばした瞬間に、何に当たったかを判定できます。

このような方法は、銃などの攻撃判定でも利用できます。


今回作るもの

今回は簡単な仕組みにします。
Spaceキーを押したら、Playerから右方向へRaycastを飛ばします。

そして Enemy へ Raycast が当たったら、Enemyへダメージを与えるようにします。

まずはRaycastを飛ばすところから作ってみましょう。


Spaceキーを押したらRaycastを飛ばそう

Playerに新しいスクリプトファイル:PlayerAttack を作成します。

まずは、関数 Shoot() を用意して Raycast を飛ばします。

public class PlayerAttack : MonoBehaviour
{    
    void Shoot()
    {
        // 2D Raycastを使用して、
        RaycastHit2D hit = Physics2D.Raycast(
            transform.position,     //開始位置をプレイヤーの位置に設定
            Vector2.right,          //方向を右に設定
            10f                     //距離を10に設定
        );
    }

}

Raycastシリーズの第1回でも確認しましたが、Raycastには主に、

  • どこから

  • どちらへ

  • どこまで

という情報を指定します。

今回の場合は、次の条件でRaycastを飛ばしています。

  • transform.position → Playerの位置から

  • Vector2.right → 右方向へ

  • 10f → 10の距離まで

続いて、Spaceキーを押したときに Shoot() を呼び出します。

void Update()
{
    if (Input.GetKeyDown(KeyCode.Space))    //スペースキーが押されたら
    {
        Shoot();    //Shoot関数を呼び出す
    }
}

ここでは、

Input.GetKeyDown(KeyCode.Space)

を使っています。
GetKeyDownは、キーが押された瞬間を調べる命令です。


Raycastが何かに当たったか確認しよう

Raycast を飛ばしたら、まずは何かに当たったか確認します。

void Shoot()
{
    RaycastHit2D hit = Physics2D.Raycast(
        transform.position,
        Vector2.right,
        10f
    );

    if (hit.collider != null)
    {
        Debug.Log("何かに当たりました");
    }
}

Raycast が Collider へ当たると、hit.collider に、当たったColliderが入ります。
何にも当たらなかった場合は、null になります。

そのため、

if (hit.collider != null)

とすると、Raycastが何かに当たったか を確認でき、当たった場合に if()で指定した内部の処理に進みます。


オブジェクトの配置

下の図のように、Player と Enemy を配置します。

Player には、上で作成した PlayerAttack のスクリプトファイルをアタッチ

Enemy には次のものを指定してください
・Tag:Enemy
・Layer:Enemy
・Collider:BoxCollider2D を追加


Enemyに当たったか確認しよう

次は、Raycast が Enemy へ当たったか確認します。
Enemy の GameObject には「Enemy」というTagを設定しておきます。

そして、次のように書きます。

if (hit.collider != null)
{
    if (hit.collider.CompareTag("Enemy"))
    {
        Debug.Log("Enemyに命中!");
    }
}

ここで使っている CompareTag("Enemy") は、そのGameObjectのTagがEnemyかを確認しています。
RaycastがEnemyへ当たると、

Enemyに命中!

とConsoleへ表示されます。


EnemyにHPを作ろう

せっかくなので、Raycast が当たったら Enemy のHPを減らしてみましょう。

Enemy にスクリプトファイル:EnemyManager を作成します。

public class EnemyManager : MonoBehaviour
{
    public int hp = 3;  // 敵の体力

    // 敵がダメージを受ける関数
    public void TakeDamage(int damage)
    {
        hp -= damage;   // 体力を減らす
        Debug.Log("Enemyの体力:" + hp);    // 体力をデバッグログに表示

        if (hp <= 0)    // 体力が0以下になったら
        {
            Destroy(gameObject);    // 敵を削除する
        }
    }
}

ここでは、

public int hp = 3;

で Enemy の HP を 3 にしています。

TakeDamage()  という関数では

public void TakeDamage(int damage)
{
    hp -= damage;
}

 引数:damage の値だけHPを減らします。
さらに、

if (hp <= 0)
{
    Destroy(gameObject);
}

とすることで、HPが0以下になったらEnemyを削除します。


Raycastが当たったらダメージを与えよう

次は PlayerAttack 側から Enemy の TakeDamage() を呼び出します。
Shoot()関数内部の、Raycast が何かに当たった時の処理を置き換えます。

if (hit.collider != null)
{
    //Raycastが何かに当たった場合、EnemyManagerコンポーネントを取得
    EnemyManager enemy = hit.collider.GetComponent<EnemyManager>();

    //もしRaycastがEnemyに当たったら
    if (hit.collider.CompareTag("Enemy"))   
    {
        //EnemyManagerのTakeDamage関数を引数:1 で呼び出す
        enemy.TakeDamage(1);   
    }
}

まず、

hit.collider.GetComponent<Enemy>();

を使って、Raycast が当たった GameObject から EnemyManager スクリプトを取得します。
そして、EnemyManager に作成した関数:TakeDamage() に引数:1 を指定して呼び出します。

enemy.TakeDamage(1);

これでRaycastがEnemyへ当たるたびに、HPが1ずつ減るようになります。

ここまでで Raycast を使った簡単な攻撃が完成しました。


Raycastの可視化

Raycastはゲーム画面には表示されません。

そのため、確認するときには、

Debug.DrawRay()

を使うと便利です。
Shoot() に 次のコードを追加します。

Debug.DrawRay(
    transform.position,     //開始位置をプレイヤーの位置に設定
    Vector2.right * 10f,    //方向を右に設定し、距離を10に設定
    Color.red,              //色を赤に設定
    1f                      //表示時間を1秒に設定
);

Sceneビューを見ると、Raycastと同じ方向に赤い線が表示されます。Raycastそのものを表示しているわけではありませんが、Raycastがどの方向へ飛んでいるかを確認しやすくなります。


hit.pointとは

ここから今回の新しい内容です。
RaycastHit2Dには、hit.collider 以外にも、さまざまな情報が入っています。
その1つが、hit.point です。
hit.pointには、RaycastがColliderへ当たった位置 の情報が入っています。

確認するには、

Debug.Log(hit.point);

と書いてみましょう。

if (hit.collider != null)
{
    EnemyManager enemy = hit.collider.GetComponent<EnemyManager>();

    Debug.Log(hit.point);   //Raycastが当たった位置をデバッグログに表示
}

Consoleには、以下のように Raycast が当たった位置が表示されます。

Raycastが当たった位置が分かると、さまざまな処理に応用できます。

例えば、

  • 弾痕を表示する

  • 火花を出す

  • 爆発を発生させる

  • ヒットエフェクトを表示する

といった処理です。
例えば Raycast が壁へ当たった場所にエフェクトを表示したい場合、hit.point の位置へエフェクトを生成できます。

今回は実際にエフェクトまでは作りませんが、Raycastが当たった場所まで取得できるということを覚えておきましょう。


hit.normalとは

もう1つ紹介するのが、

hit.normal

です。
normalは日本語では、法線と呼ばれます。
少し難しい言葉ですが、最初は当たった面がどちらを向いているかを表すものと考えると分かりやすいです。

例えば Raycast が壁へ当たった場合、hit.point が、当たった位置を表すのに対して、hit.normal は当たった面の向きを表します。


hit.normalをSceneビューで確認しよう

Debug.DrawRay()を使うと、hit.normalも確認できます。

if (hit.collider != null)
{
    Debug.DrawRay(
       hit.point,      //Raycastが当たった位置を開始位置に設定
       hit.normal,     //Raycastが当たった位置の法線方向を方向に設定
       Color.green,    //色を緑に設定
       1f              //表示時間を1秒に設定
   );
}

ここでは、hit.point から、hit.normal の方向へ線を表示しています。

Sceneビューを見ると、Raycastが当たった面から線が伸びていることを確認できます。


hit.normalは何に使える?

hit.normalを使うと、当たった面の向きが分かります。

そのため、

  • 弾を跳ね返す

  • レーザーを反射させる

  • 壁に沿った方向を調べる

  • エフェクトの向きを合わせる

  • 壁にオブジェクトを貼り付ける

といった処理に利用できます。

例えば、レーザーが放射状に発射される場合や、壁が傾いている場合に反射レーザーを作る際も、hit.normalが重要になります。

今回は、

  • hit.point → 当たった場所

  • hit.normal → 当たった面の向き

という違いを覚えておけば十分です。


Raycastには仲間もある

ここまで使ってきた Raycast は、細い線を飛ばしてColliderを調べる仕組みでした。
しかしUnityには、Raycast に似た方法もいくつか用意されています。

最後に簡単に紹介しておきます。


RaycastAll

通常の Raycast() では、Raycastが最初に当たったColliderの情報を取得します。
例えば、

Player ─── Enemy ─── Enemy ─── Wall

と並んでいた場合、最初に当たったEnemyの情報を取得します。

一方、Physics2D.RaycastAll() を使うと、Rayの途中にある複数のColliderを取得できます。

Player ─── Enemy ─── Enemy ─── Wall
            ↑          ↑          ↑
          すべて調べる

例えば、敵を貫通するレーザーなどを作るときに利用できます。


CircleCast

Raycast は細い線ですが、Physics2D.CircleCast() を使うと、円の大きさを持った判定を飛ばせます。
Raycastよりも少し広い範囲を調べたい場合に利用できます。

例えば、

  • キャラクターの前方を広めに調べる

  • 敵を見つける

  • 少し太さのある攻撃判定を作る

といった処理に使えます。


BoxCast

さらに、Physics2D.BoxCast() を使うと、四角い範囲で調べることができます。

例えばキャラクターと同じくらいの大きさで、この先へ進めるかを調べたいときにも利用できます。


今回のまとめ

今回は、Raycastを使った攻撃について確認しました。


こちらはUnity初心者向けの記事をテーマ別にまとめています。
▼ Unity初心者向け記事まとめはこちら


こちらのマガジン「ミニゲーム作成講座 全8回セット」でもシーンの切り替えなどを活用して、初心者でも知識ゼロから作れるミニゲーム作成講座を紹介しています。

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


ファイブボックスでは、スクラッチやUnityの個別指導のオンラインレッスンを行っています。
ご興味のある方は当サイト、オンラインレッスンから、無料体験授業へお問い合わせ下さい。

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

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