Unityでの2Dジャンプアクションゲームの作成(敵キャラクターの落下防止と当たり判定)


前回は敵キャラのプレファブ化と移動を実装したので今回は敵キャラの落下防止とPlayerと衝突した時の当たり判定について書いていきます。

まずは敵キャラが穴を検知したら落ちずに反転する動作を追加します。

Playerのジャンプを実装した時と同じように、Enemyが地面に触れてなければ方向転換をするようにします。
Playerは中心からベクトルを出しましたが敵キャラは早めに方向転換するため先っぽからベクトルを作ります。

まずはPrefab内のEnemyを選択してEnemy Management(Script)のStage LayerをNothingからstageへ変更します。

Playerと同じようにEnemyが地面と接しているかどうかをチェックするGroundChk()をEnemyManagement.csへ記述していきます。

    bool GroundChk()
    {
        // transform.localScaleの正負によってEnemyをx方向に反転する
        Vector3 scale = transform.localScale;
        // 始点が常にEnemyの進行方向に出るようにscal.xを乗算する
        Vector3 startposition = transform.position + transform.right * 0.3f * scale.x;
        // startpostionから足元までを終点とする
        Vector3 endposition = startposition - transform.up * 0.55f;

        // Debug用に始点と終点を表示する
        Debug.DrawLine(startposition, endposition, Color.red);

        // Physics2D.Linecastを使い、ベクトルとStageLayerが接触していたらTrueを返す
        return Physics2D.Linecast(startposition, endposition, StageLayer);
    }

Playerの時は中心を始点としましたが、落下を避けるためのプログラムなので先端を始点として垂直に足元に終点を作っています。
後はデバッグ用表示をDebug.DrawLineで行い、地面と接したらTrueを返すようなプログラムにします。
再生した後にSceneをクリックしてベクトルの状態を確認します。

transform.localScaleは縮小拡大や回転を行うものであり、transform.localScale.xは右に進んでいるときは正左に進んでいるときは負となります。この数字を乗算する事でベクトルの始点を常に進行方向に設定することができます。

Update内でGroundChk()を実行して、地面に触れていなければ方向転換をするプログラムを記述します。

    private void Update()
    {
        // 地面に触れてなければ方向転換
        if (! GroundChk())
        {
            // 方向転換をする
            ChgDIrection();
        }
    }

    void ChgDIrection()
    {
        // 右に移動している時は
        if (move == MOVE_TYPE.RIGHT)
        {
            // 左へ方向転換する
            move = MOVE_TYPE.LEFT;
        }
        else
        {
            // 左へ移動中は右へ方向転換する
            move = MOVE_TYPE.RIGHT;
        }
    }

敵キャラは右に向いているので初期の進行方向を右に変更しときます。
スクリプト全体はこんな感じ。

EnemyManagement.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class EnemyManagement : MonoBehaviour
{
    public LayerMask StageLayer;

    // 動作状態を定義する 
    private enum MOVE_TYPE
    {
        STOP,
        RIGHT,
        LEFT,
    }
    MOVE_TYPE move = MOVE_TYPE.RIGHT; // 初期状態は右へ
    Rigidbody2D rbody2D;             // Rigidbody2Dを定義
    float speed;                     // 移動速度を格納する変数

    private void Start()
    {
        // Rigidbody2Dのコンポーネントを取得
        rbody2D = GetComponent<Rigidbody2D>();
    }

    private void Update()
    {
        // 地面に触れてなければ方向転換
        if (! GroundChk())
        {
            // 方向転換をする
            ChgDIrection();
        }
    }

    bool GroundChk()
    {
        // transform.localScaleの正負によってEnemyをx方向に反転する
        Vector3 scale = transform.localScale;
        // 始点が常にEnemyの進行方向に出るようにstartpositionを決める
        Vector3 startposition = transform.position + transform.right * 0.3f * scale.x;
        // startpostionから足元までを終点とする
        Vector3 endposition = startposition - transform.up * 0.55f;

        // Debug用に始点と終点を表示する
        Debug.DrawLine(startposition, endposition, Color.red);

        // Physics2D.Linecastを使い、ベクトルとStageLayerが接触していたらTrueを返す
        return Physics2D.Linecast(startposition, endposition, StageLayer);
    }

    // 方向転換をする
    void ChgDIrection()
    {
        // 右に移動している時は
        if (move == MOVE_TYPE.RIGHT)
        {
            // 左へ方向転換する
            move = MOVE_TYPE.LEFT;
        }
        else
        {
            // 左へ移動中は右へ方向転換する
            move = MOVE_TYPE.RIGHT;
        }
    }

    // 物理演算(rigidbody)はFixedUpdateで処理する
    private void FixedUpdate()
    {
        // Playerの方向を決めるためにスケールの取り出し
        Vector3 scale = transform.localScale;
        if (move == MOVE_TYPE.STOP)
        {
            speed = 0;
        }
        else if (move == MOVE_TYPE.RIGHT)
        {
            scale.x = 1; // 右向き
            speed = 3;
        }
        else if (move == MOVE_TYPE.LEFT)
        {
            scale.x = -1; // 左向き
            speed = -3;
        }
        transform.localScale = scale; // scaleを代入
        // rigidbody2Dのvelocity(速度)へ取得したspeedを入れる。y方向は動かないのでそのままにする
        rbody2D.velocity = new Vector2(speed, rbody2D.velocity.y);
    }
}

動作の確認をするとこんな感じ。

Sceneから地面と触れている判定がちゃんとできているか確認するとこんな感じ。

進行方向の先端に赤い線が出ており地面から離れると方向転換をしている事が確認できました。

次に敵キャラに当たった時にPlayerキャラが消滅するような動作を追加していきます。

まずは先ほど作った敵キャラにtagを付けていきます。
Open Prefabを開いてtagを選択、新しくEnemy tagを作るのでAdd tagを選択します。

Enemy tagを追加します。

tagを追加したらInspetorよりEnemy tagを敵キャラに追加していきます。

tagの追加が終了したので、敵キャラにぶつかったらplayerが消滅するようにしていきます。
EnemyのCircle Collider 2DのIs Triggerにチェックを入れることで接触を検知してトリガーで通知できる…と思いきや床をすり抜けてしまいました。

どうやらCircle Collider 2DのIs TriggerをOnにすると地面との衝突判定がなくなってしまうみたいです。

というわけでEnemyへAdd ComponentよりBox Collider 2Dを追加し Is Triggerにチェックを入れます。
地面との衝突判定はCircle Collider 2Dで行いPlayerとの衝突判定はBox Collider 2Dで行うようにしていきます。

circle ColliderとBox ColliderのOffsetとSizeを変えてキャラクターとの当たり判定を調整します。Box ColliderよりCircle COlliderの方が大きいとトリガーの検知が出来ないのでそうならないように気を付けます。こんな感じ。

敵キャラに接触した時に消滅する処理をPlayerManagement.csに追加します。以下のようにOnTriggerEnter2DDestroyを使って実装していきます。

PlayerManagement.cs
    // トリガーが発生した時の処理
    private void OnTriggerEnter2D(Collider2D collision)
    {
        // 接触したオブジェクトのtag名がEnemyの場合は
        if(collision.gameObject.tag == "Enemy")
        {
            // Playerオブジェクトを消去する
            Destroy(this.gameObject);
        }
    }

再生して動作を確かめるとPlyaerキャラのカエルくんが敵キャラに当たると消滅する事が確認できました。

次回はPlayerが消滅した後にリスタートするような動作を作っていきます