Roba Memo - 素人のUnity覚書と奮闘記

素人のUnity覚書と奮闘記

Mobをルート通りに移動させるには1 キューブを移動させる

ディフェンスゲームの最初の壁。ルート通りに移動する方法。
四苦八苦した結果、それっぽいのができたのでメモ。
ちなみに、ナビゲーションは使いません。

1:マップを配置

Canvasではなくて、ヒエラルキーに、そのままスプライトを配置する。

2:方向転換用キューブを配置

開始点とコーナーにキューブを配置する。
下の画像の黒いのがキューブです。
左下が開始点。
f:id:nico-taniku:20170808192805p:plain:h300
このキューブには、Box Colliderが既に設定されているので、このまま使う。
このキューブに当たったら、方向転換させる。

3: Mob用のキューブを配置

(1)開始位置に、重なるようにキューブを配置する。
(2)手順2のキューブとの当たり判定を取りたいので、Rigidbodyを追加する。
(3)物理演算は必要ないので、Is Kinematicにチェックを入れる。
(4)Box ColliderのIs Triggerにチャックを入れる。
f:id:nico-taniku:20170808191616p:plain:w300

4:方向を指定するには

方向転換キューブに当たったときに、キューブが持つ方向をMobに渡してあげるようにしたい。
(1)まず、クラスを作成し、手順2のキューブに方向を保持させる。

1=top 上方向
2=bottom 下方向
3=left 左方向
4=right 右方向

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DirectionChanger : MonoBehaviour
{
    [Header ("進行方向 T=1 D=2 R=3 L=4")]
    public int direction;

}

(2)キューブにクラスをアタッチして、方向を指定する。
プレハブにしておくと、後々楽かも。
f:id:nico-taniku:20170818082058p:plain:w300

5:Mobを一定方向に移動させる

(1)とりあえず、Mobクラスを作成して、Mobキューブにアタッチしとく。

(2)次は計算方法。
Mobを一定速度で目的地に向かって直線移動させる。
移動速度は、Mob毎で変化させたいからインスペクターで変更できるようにしておこう。
1秒間の移動量=移動速度
Updateで処理をするので、それにTime*deltaTimeを掛ければ1フレーム毎の移動量が算出できるはず。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Mob : MonoBehaviour
{

    [SerializeField][Header ("移動速度")] float walk_speed;
    //移動量
    float distance;


    void Update ()
    {
        distance = walk_speed * Time.deltaTime;
    }
}

(3)distanceを座標に加算する。
どの方向に進んでいるかで、加算する座標が変わるので、条件分岐で処理する。

[SerializeField][Header ("移動速度")] float walk_speed;
//移動量
float distance;
//移動方向
string direction;

void Update ()
{
    distance = walk_speed * Time.deltaTime;

    if (direction == 1) {
        transform.localPosition += new Vector3 (0, distance, 0);
    } else if (direction == 2) {
        transform.localPosition += new Vector3 (0, -distance, 0);
    } else if (direction == 3 ) {
        transform.localPosition += new Vector3 (distance, 0, 0);
    } else if (direction == 4) {
        transform.localPosition += new Vector3 (-distance, 0, 0);
    }
}

これで、direction変数で指定した方向に移動するようになった。

6:当たり判定で、何に当たったかを見分ける

当たり判定は、OnTriggerEnter()を使う。
参照 : Unity - スクリプトリファレンス: Collider.OnTriggerEnter(Collider)

Mobが辿るルートを複数に分けたいから、どのルートのコライダーに当たったかを判定したい。
これをタグで見分けることにした。
(1)route1というタグを作り、方向転換キューブに設定する。
f:id:nico-taniku:20170809075309p:plain:w300

(2)次は、スクリプト
判定を取りたいタグは、Mobごとで変わるから、インスペクターで設定できる変数にしておく。

[SerializeField][Header ("ルートのタグ名")] string route_tag;
//ヒットしたゲームオブジェクト
GameObject target;

void OnTriggerEnter (Collider coll)
{
    target = coll.gameObject;
    string tag = target.tag;

    if (tag == route_tag) {
        //ここに処理
    }
}

7:方向転換させる

DirectionChangerクラスのdirectionの値を読んで、Mobクラスのdirectionに代入する。
そうしたら、先に書いたUpdateが方向転換してくれるはず。

[SerializeField][Header ("移動速度")] float walk_speed;
//移動量
float distance;
//移動方向
string direction;

[SerializeField][Header ("ルートのタグ名")] string route_tag;
//ヒットしたゲームオブジェクト
GameObject target;

void Update ()
{
    distance = walk_speed * Time.deltaTime;

    if (direction == 1) {
        transform.localPosition += new Vector3 (0, distance, 0);
    } else if (direction == 2) {
        transform.localPosition += new Vector3 (0, -distance, 0);
    } else if (direction == 3 ) {
        transform.localPosition += new Vector3 (distance, 0, 0);
    } else if (direction == 4) {
        transform.localPosition += new Vector3 (-distance, 0, 0);
    }
}

void OnTriggerEnter (Collider coll)
{
    target = coll.gameObject;
    string tag = target.tag;

    if (tag == route_tag) {
        DirectionChanger directionChanger = target.GetComponent<DirectionChanger> ();
        direction = directionChanger.direction;
    }
}

!問題発生 当たった瞬間に方向転換するので、道の上を歩いてくれない!!

8:道の真ん中を歩かせたい

手っ取り早いのは、Box Collierのサイズを調整すればいい。
それも1つの方法だと思うけど、今回は、道の真ん中まで進んでから方向転換させてみる。

(1)方向転換キューブのPivotを中心点に設定しておく。

(2)当たったら、まずは方向転換せずに進ませて、座標が方向転換キューブの座標以上(場合によっては以下)になったら、directionの値を変更する。
処理内容としては、

・OnTriggerEnter()にて、方向転換キューブのdirectionを次の方向として変数に保管しておく。

・OnTriggerEnter()にて、方向転換キューブの座標を目標座標として、変数に保管する。

・Updateにて、衝突してる場合→目標座標に到達した場合→Mobクラスのdirection=次の方向 という処理を追加したい。

・そのためには、衝突したかどうかを判定する変数が必要。
 衝突したらtrue、Mobクラスのdirectionに次の方向を代入したらfalse。

(3)開始位置では、上記の処理をせずに指定された方向へ進ませたい。
最初はMobクラスのdirectionはnullになっているので、nullならdirectionにそのまま代入させる。
stringのnull判定をとる方法

string.IsNullOrEmpty (変数);

(4)これらをスクリプトにする。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Mob : MonoBehaviour
{
    [SerializeField][Header ("ルートのタグ名")] string route_tag;
    [SerializeField][Header ("移動速度")] float walk_speed;
    //移動方向
    string direction;
    //移動量
    float distance;
    //ヒットしたゲームオブジェクト
    GameObject target;
    //チェンジャーにヒットしたかどうか
    bool change;
    //次の開始位置
    Vector3 next_position;
    //次の方向
    string next_direction;

    void Update ()
    {
        distance = walk_speed * Time.deltaTime;

        if (direction == 1) {
            transform.localPosition += new Vector3 (0, distance, 0);
            if (change) {
                if (transform.position.y >= next_position.y) {
                    direction = next_direction;
                    change = false;
                }
            }
        } else if (direction == 2) {
            transform.localPosition += new Vector3 (0, -distance, 0);
            if (change) {
                if (transform.position.y <= next_position.y) {
                    direction = next_direction;
                    change = false;
                }
            }
        } else if (direction == 3) {
            transform.localPosition += new Vector3 (distance, 0, 0);
            if (change) {
                if (transform.position.x >= next_position.x) {
                    direction = next_direction;
                    change = false;
                }
            }

        } else if (direction == 4) {
            transform.localPosition += new Vector3 (-distance, 0, 0);
            if (change) {
                if (transform.position.x <= next_position.x) {
                    direction = next_direction;
                    change = false;
                }
            }
        }
    }

    void OnTriggerEnter (Collider coll)
    {
        target = coll.gameObject;
        string tag = target.tag;

        if (tag == route_tag) {
            DirectionChanger directionChanger = target.GetComponent<DirectionChanger> ();
            if (string.IsNullOrEmpty (direction)) {
                direction = directionChanger.direction;
            } else {
                next_direction = directionChanger.direction;
                next_position = target.transform.position;
                change = true;
            }
        } 
    }
}

9:停止させる

停止タグを作って、そのタグがついたオブジェクトに衝突したら停止させるようにする。
f:id:nico-taniku:20170809095050p:plain:w300f:id:nico-taniku:20170809095056p:plain:w300

移動する処理はUpdateに書いてあるので、IsTriggerEnterからUpdateに停止を知らせなければならない。
ということで、停止フラグを作成する。

//停止フラグ
bool is_stop;

void Update ()
{
    if (is_stop) {
        //停止時の処理
    } else {
        //移動の処理(省略)
    }
}

void OnTriggerEnter (Collider coll)
{
    target = coll.gameObject;
    string tag = target.tag;

    if (tag == route_tag) {
        //方向転換キューブに衝突した処理(省略)
    } else if (tag == "stop") {
        //停止キューブに衝突した処理
        is_stop = true;
    }
}

とりあえず、ここまで。