【Unity2D】Linecastを使って前方の判定を取る

Unity2D

Unity2D ARPG

こんにちは。なおキーヌです。

ブログ毎日更新は325日目になります。

前回「【Unity2D】Deubg.DrawLineを使って覚えよう」にて掴む処理の基本的な仕組み作りを行いました。

今回使う「Physics2d.Linecast」は前回使って実際に判定を取ります。

「Debug.DrawLine」と使い方は殆ど同じです。

第三引数がレイヤーかカラーかの違いなので、視覚的に判定の表示もしておきたいので併用していきます。

それでは早速Linecastを使って判定を取って掴む処理を作っていきましょう。

まずはDebug.DrawLineで検証

前回のコードはちょっと判定が大きすぎたので調整します。

理想はプレイヤーの3ドットぐらい前方までにしておきたいですね。

これは微調整できるようにしておきましょう。

あまりにも大きいと何もない空間で掴む処理に移ってしまいます。

なにはともあれ、実装していきます。

キャラクターが正方形ではなく長方形なので、上下と左右で出る長さを変更する必要がありますね。

これはswitch分岐でそれぞれの値を設定するか定数にしておけばOKです。

後は発生地点がキャラクターの完全中央にしたいので調整します。

    // updateの中でnewしたくなかったのでインスタンス変数にした
    Vector3 castPosition = new Vector3();
    Vector3 castPositionDistance = new Vector3();


    void FixedUpdate() {
        this.transform.Translate(vx/50, vy/50, 0);

        switch(nowAnim - nowAnim%10) {
            case 0:
                // 離れた距離は1fで十分だった
                castPositionDistance = transform.up * 1f;
                // Vector3dの.Set関数はVector3を入れると値がセットされる
                castPosition.Set(transform.position.x,(transform.position.y-5.5f) - 10.5f,0f);
                break;
            case 10:
                castPositionDistance = transform.up * -1f;
                castPosition.Set(transform.position.x,(transform.position.y-5.5f) + 10.5f,0f);
                break;
            case 20:
                castPositionDistance = transform.right * 1f;
                castPosition.Set(transform.position.x-8f,(transform.position.y-5.5f),0f);
                break;
            case 30:
                castPositionDistance = transform.right * -1f;
                castPosition.Set(transform.position.x+8f,(transform.position.y-5.5f),0f);
                break;
            default:
                break;
        }

        Debug.DrawLine(
                castPosition,
                castPosition - castPositionDistance,
                Color.red
        );
    }

前回directというローカル変数を使ってたのですが、update系の中でnewをしたくなかったので
インスタンス変数でVector3をnewしておいてその変数を使いまわすといった感じにしました。

transform.positionの基準点はスプライトの大きさになってしまうので、
実際の見えているスプライトの大きさにBoxCollider2Dで囲むと、私の場合16×21の大きさになりました。

そして基準点が上にズレてみえるので、BoxCollider2DのY座標をずらして
スプライトの見えている部分にサイズと位置を合わせているため、Set関数を使う時それと同じだけY座標をずらしています。

ちょっと意味が分かり辛いかもしれませんがねこれ……

もうちょっと良い言い回しが見つかったら更新します(笑)

後はそのままだと自分自身の中心から線が出てしまうので、発生位置をずらします。

16×21だと左右の場合はxを8f、上下の場合は10.5fずらせばBoxCollider2Dの外側から発生するようになります。

Unity2D ARPG

恐らくこれでも触れている状態になっているかもしれません。

判定をとるレイヤーを指定してしまえば問題はなさそうですのでこのままいきましょう。

Linecastを使ってみる

やっと本題に入ります。

前回にも今回の最初にも言いましたが、LinecastとDrawLineの第一引数と第二引数は同じです。(始点と終点)

なのでそのまま値を使いまわせますね。

残りはレイヤーの指定ですね。

レイヤーはWallとして新しく作りましょう。

そして判定を取るのはそのWallレイヤーにしてしまえばプレイヤーと重なってても大丈夫です。

レイヤーの作り方はタグの作り方とほぼ同じなので省略します。

そしてタグはGrabOKにしておけば条件式で

壁かどうか

掴めるかどうか

というスクリプトを組めるようになります。

それでは早速Linecastで判定を取ってみましょう


    // 衝突判定が取れたかどうかを格納する変数
    RaycastHit2D is_lcast;


    void FixedUpdate() {
        this.transform.Translate(vx/50, vy/50, 0);

        switch(nowAnim - nowAnim%10) {
            case 0:
                // 離れた距離は1fで十分だった
                castPositionDistance = transform.up * 1f;
                // Vector3dの.Set関数はVector3を入れると値がセットされる
                castPosition.Set(transform.position.x,(transform.position.y-5.5f) - 10.5f,0f);
                break;
            case 10:
                castPositionDistance = transform.up * -1f;
                castPosition.Set(transform.position.x,(transform.position.y-5.5f) + 10.5f,0f);
                break;
            case 20:
                castPositionDistance = transform.right * 1f;
                castPosition.Set(transform.position.x-8f,(transform.position.y-5.5f),0f);
                break;
            case 30:
                castPositionDistance = transform.right * -1f;
                castPosition.Set(transform.position.x+8f,(transform.position.y-5.5f),0f);
                break;
            default:
                break;
        }

        Debug.DrawLine(
                castPosition,
                castPosition - castPositionDistance,
                Color.red
        );

        if(Input.GetKeyDown(KeyCode.G)) {

            is_lcast = Physics2D.Linecast(
                castPosition,
                castPosition - castPositionDistance,
                8
            );
            Debug.Log(is_lcast);
        }


    }

……普通にDebug.Logしちゃうと

UnityEngine.RaycastHit2D
UnityEngine.Debug:Log(Object)

ってなって中身がどうなっているのかわかりませんね。

is_lcastを「is_lcast.collider」に変えてみましょう。

すると判定されてないときはNullになります。

……そしてまた取れない。

オブジェクト側のLayerも設定したのにNullのまま。

数十分頭をフル回転させたりググりまくった結果、

「[SerializeField]」

でLayerMaskの変数を定義し、プロパティ側でレイヤーマスクを作ってあげて
この変数をレイヤーマスクの引数に渡すことで判定することができました。

この辺のマスク的な仕組みなのでもうちょっと勉強する必要がありますね……

とりあえず判定が取れたので一旦これでヨシとしましょう。


    // Unityエディタのプロパティで設定できるレイヤーマスク
    [SerializeField]
    private LayerMask layerMask;


    void FixedUpdate() {
        this.transform.Translate(vx/50, vy/50, 0);

        switch(nowAnim - nowAnim%10) {
            case 0:
                // 離れた距離は1fで十分だった
                castPositionDistance = transform.up * 1f;
                // Vector3dの.Set関数はVector3を入れると値がセットされる
                castPosition.Set(transform.position.x,(transform.position.y-5.5f) - 10.5f,0f);
                break;
            case 10:
                castPositionDistance = transform.up * -1f;
                castPosition.Set(transform.position.x,(transform.position.y-5.5f) + 10.5f,0f);
                break;
            case 20:
                castPositionDistance = transform.right * 1f;
                castPosition.Set(transform.position.x-8f,(transform.position.y-5.5f),0f);
                break;
            case 30:
                castPositionDistance = transform.right * -1f;
                castPosition.Set(transform.position.x+8f,(transform.position.y-5.5f),0f);
                break;
            default:
                break;
        }

        Debug.DrawLine(
                castPosition,
                castPosition - castPositionDistance,
                Color.red
        );

        if(Input.GetKeyDown(KeyCode.G)) {

            is_lcast = Physics2D.Linecast(
                castPosition,
                castPosition - castPositionDistance,
                layerMask
            );
            Debug.Log(is_lcast.collider);
        }


    }

Unity2D ARPG

こんな感じに判定が取れたらOKです。

これでずっとぶつかっていなくても前方に判定があれば取れるようになりました。

余談ですがこれをうまく使えば敵をつかむという仕組みも作れちゃいますね。

こういう場合もうちょっと線を伸ばして猶予を付けておかないと無理ゲーになりそうです。

次回はこの判定を使って掴む処理を作っていきましょう。

それでは。