fixelのブログ

Unity&C#初心者がイチからRTSを作るまでを書き記す日記

5回目・NavMeshPathとは何ぞや?

目次

Unityの公式サイトのここを読んでみると、

http://docs.unity3d.com/ja/current/ScriptReference/NavMeshPath.html

移動方向の矢印を作るにはNavMeshPathのcornersを使うことになりそうなことまでは把握した。

ただ、その他のことは全く書いてないので手探りで理解することに。

 

分かったこと1・「NavMeshPath」は「NavMeshAgent」の中にある

    public NavMeshAgent agent;
    agent = GetComponent<NavMeshAgent>();
    NavMeshPath path = agent.path;

これと

    NavMeshPath path;

これは全く一緒らしい。

agentをpublicしたってとこは違うけどほぼ一緒のことをやってるらしい。

 

分かったこと2・「NavMeshPath.corner」の扱いとVector3の使い方

public Vector3 corners;

とUnityのサイトのNavMeshPath.cornerには書いてあるけど、

これは「corners」はVector3で書かれているってことを示しているらしい。

Vector3[]は、

Vector3[n] = (x, y, z)みたいな感じで作られていて、

Unityのサイトの感じでpath.corners [0]のxだけを参照したい時は、

path.corners [0].xとすればいいっぽい。

 

分かったこと3・「NavMeshAgent.destination」を設定してもすぐには「NavMeshPath.corner」は作られない

「NavMeshAgent.destination」設定後すぐに「NavMeshPath.corner」を参照しようとすると、そんなもんないと言われます。

「NavMeshAgent.hasPath」を確認することでこれが回避できます。

 

分かったこと4・「NavMeshPath.corner」は自動更新されない


「NavMeshAgent.destination」を設定してPlayerが移動中の場合、「NavMeshPath.corner」はどうなっているかというと、

全く更新されません。

始点と終点は直線上にあるときだと、corner[0]に始点の座標、corner[1]に終点の座標が書いてあるだけです。

「NavMeshAgent.destination」を新しく設定すれば更新されます。

4回目・プレイヤーの変数を別のオブジェクトが参照する

C#のプレイヤー側のスクリプト名を「Player」、

別のオブジェクト側のスクリプト名を「Cursor」とし、

「Player」側で下記のプログラムを書いて、

direction = (GameObject)Resources.Load ("Prefabs/Cursor");
GameObject cursor = (GameObject)Instantiate (direction);
cursor.transform.parent = transform;

「Cursor」をプレハブから生成した後、

「Cursor」のparentを「Player」にすることで、

「Player」の取得をGetComponentではなく、GetComponentInParentで取得できるようにしてます。

 

「Player」側のスクリプトの変数

    public NavMeshAgent agent;
    public RaycastHit hit;
    public bool once = false;
    public bool targetF = false;

 

「Cursor」側のスクリプトの一部

    private Player _parent;

    void Start() {
        _parent = GetComponentInParent<Player>();
        transform.parent = null;
    }

    void Update() {
        if (_parent.once) {
            Object.Destroy (gameObject);

    } else {
            NavMeshPath path = _parent.agent.path;
            Vector3 _parentPO = _parent.transform.position;

 こうすることで「Cursor」が「Player」の値を参照できるようになりました。

「transform.position」は元々PublicでUnity側が作ってくれているので書かなくても参照できる。

3回目・視野内に入ったプレイヤーを感知して突進する敵AI

    public GameObject player;
    private NavMeshAgent agent;

    void Start ()
    {
        player = GameObject.Find("Player");
        agent = GetComponent<NavMeshAgent>();
    }

    void Update ()
    {
        Vector3 forward = transform.TransformDirection(Vector3.forward);
        Vector3 targetDirection = player.transform.position - transform.position;
        float distance = targetDirection.magnitude;
        float angle = Vector3.Angle(forwardtargetDirection);

        if (distance < 4 && angle < 90)
            agent.SetDestination(player.transform.position);
    }

    void OnTriggerEnter(Collider other)
    {
        if(other.tag == "Player")    
        {
            Debug.Log ("hit");
        }
    }

 これだけで探査型AIの出来上がり〜

2回目・クリックした位置にプレイヤーを移動させる

    private NavMeshPath path;
    public NavMeshAgent agent;
    public RaycastHit hit;
    public bool once = false;
    public bool targetF = false;
 

     if (Input.GetMouseButtonDown (0)) {

            if (Physics.Raycast (Camera.main.ScreenPointToRay (Input.mousePosition), out hit100)) {
                if (hit.collider.isTrigger) {//only player&enemy have isTrigger.
                    agent.destination = new Vector3(hit.transform.position.x0hit.transform.position.z);
                    targetF = true;
                } else {
                    agent.destination = hit.point;
                    targetF = false;
                }
                once = true;
            }

どういうことをやっているかというと、

agent.destination = new Vector3(hit.transform.position.x0hit.transform.position.z);

敵キャラにクリックした場合は、敵の位置に向かって移動する。

agent.destination = hit.point;

地面をクリックした場合は、クリックした場所に向かって移動する。

ということをしている。

地面はcollider.isTriggerをOFFにし、敵やプレイヤーはONにすることで区別しています。

 

もしもこの条件分岐がなく、「agent.destination = hit.point;」だけにした場合は、

敵にクリックした時、そのクリックした位置(側面)のy座標を0にしたポイントがdestinationに設定されるため、

敵に向かって移動、ということにはなりません。

 

試しにagent.destination = hit.point;」だけにしてプレイヤーをクリックすると、停止するのではなく移動してしまうのがわかるかと思います。

 

そういうわけでこんな感じのコードになりました。

1回目・Navigationのポジションのズレを直す

ナビゲーションをSphere + Planeで普通に作っていくと

f:id:fixel-sub:20151117200914p:plain

こんな感じになるのだけど、

これをBakeして実行すると下の画像みたいにY座標がズレてしまう

f:id:fixel-sub:20151117201544p:plain

補正のせいかY=0.666...という面倒なことになった。

 

色々と試行錯誤していった結果、

Navigation -> Bake ->「Agent Radius」の値を小さくしていけばズレが小さくなっていくが、値を0にできないためこの方法ではズレを解消することができなかった。

 

適当にぽちぽち押していった結果、

Navigation -> Bake -> Advanced ->「Height Mesh」をONにするといいらしい

f:id:fixel-sub:20151117202856p:plain

これだけで補正がなくなってスッキリ

「Agent Radius」がどんな値でもtransformのポジションYが変わらなくなりました

 

f:id:fixel-sub:20151117203113p:plain

0回目・はじめに:このブログが目指すところ & 目次

Unity & C#初心者の私が、

ゲームを完成させてApp StoreiPhoneアプリ公開するまでの努力を見せるだけのブログです。

どんなRTSリアルタイムストラテジー)を作りたいかというと、

  • 決戦2のようなゲーム

 

いやー高校生くらいの時に親父が500円でワゴンから買ってきたこのゲームにメチャハマりましたなー懐かしい。

何人がお分りいただけただろうか?

ゲーマーでも1%の人しか知らないような超マイナーゲーなのでどんなシステムかと言うと、

  • 複数の軍隊を指揮して敵チームに勝利する
  • 自分の軍隊の移動はこちらが完全に指示できる
  • 攻撃は軍隊同士がぶつかれば自動で行う
  • 移動、攻撃もリアルタイムで進行

 

ファイアーエムブレムファミコンウォーズのようなターン制だったり、

無双シリーズのような個人を動かすゲームではなく、

決戦初代 とか 三国志大戦戦国大戦のようなリアルタイムで軍隊を指揮するゲームを目指します。

 

それに必要なものは、

  • 地形を察知して目的地まで移動する
  • クリックした位置にプレイヤーを移動させる
  • 視野内に入ったプレイヤーを感知して突進する敵AI
  • 移動経路を矢印で表示する←今ここ
  • 視野をグラフィックで表示する
  • 選択したプレイヤーに移動を指示する
  • 視野内に入った敵のグラフィック表示させるようにする
  • 敵捜索AIを敵に付ける

 

ゲーム作りが進んでいって必要なものが増えたら付け足します。

少ないように見えて結構難解なプログラムを書かされるかも?なのでとりあえず完走目指して頑張ります。