質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.99%

  • Unity

    3036questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity2D

    555questions

移動しているのにNavMeshAgent.velocityが初回だけ0になる

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 358

nico29

score 65

やりたいこと

Unityで2Dゲームを作っています。
NevMeshを使ってキャラクター(エージェント)を動かす際に、スプライトアニメーションを再生したいのですが、初回の移動だけうまくいきません。

質問

NaMeshを使ってキャラクターを移動させた際、初回だけ、移動するのにNavMeshAgent.velocityが(0.0, 0.0, 0.0)になってしまいます。
2回目以降の移動では(1.0, 0.0, -0.2)という風に、値が入ってきます。
velocityだけではなくhasPathなども試してみたんですが、同様に初回だけfalseになってしまいます。
ちゃんと移動はしているのにパスがないって、どういうこと?って感じなのですが、こういうものなのでしょうか?
NavMeshを初めて使うので、何か変なところがないか見ていただけますでしょうか。

該当するコード

キャラクターをドラッグして、MouseUpしたときにルート上なら、一旦ドラッグ開始地点に戻してから移動させてAnimatorを操作(Walk())します。
ドラッグ部分は問題ないので省略しています。当たり判定も問題ありません。

変数start_positionはドラッグ開始時のpositionを代入しています。
ドラッグしたキャラクターを最初の位置に戻すためです。

Characterクラス

    Animator animator;
    NavMeshAgent agent;
    Vector3 start_position;

    void Update ()
    {
        if (Input.GetMouseButtonUp (0)) {
            OnStopDrag ();
        }
    }

    void OnStopDrag ()
    {    
            Ray ray = Camera.main.ScreenPointToRay (Input.mousePosition);
            RaycastHit hit_info = new RaycastHit ();
            float max_distance = 100f;
            int layer = LayerMask.NameToLayer ("Route");
            int layer_mask = 1 << layer;
            bool is_hit = Physics.Raycast (ray, out hit_info, max_distance, layer_mask); 

            transform.position = start_position;
            if (is_hit) {
                if (hit_info.transform.name.IndexOf ("DirectionChanger") != -1) {
                    //移動開始
                    agent.SetDestination (hit_info.point);
                    StartCoroutine (Walk ());
                }
            }
    }

    IEnumerator Walk ()
    {
        animator.SetBool ("attack", false);
        animator.SetBool ("stop", false);

        while (true) {    
            Debug.Log (agent.velocity);

            if (agent.velocity.z > 0.5f) {
                animator.SetInteger ("direction", 1);
            } else if (agent.velocity.z < -0.5f) {
                animator.SetInteger ("direction", 2);
            } else if (agent.velocity.x > 0.5f) {
                animator.SetInteger ("direction", 3);
            } else if (agent.velocity.x < -0.5f) {
                animator.SetInteger ("direction", 4);
            }
            now_direction = animator.GetInteger ("direction");

            if (agent.velocity == Vector3.zero) {
                animator.SetBool ("stop", true);
                yield break;
            }
            yield return null;
        }
    }

コンポーネント

コンポーネント

出力結果

初回 (0.0, 0.0, 0.0)
Walkコルーチンはvelocityが0になったら抜けるようにしてあるので1度しか呼び出されません。
もちろん、アニメーションは変化しません。
移動はします。

2回目(1.0, 0.0, -0.2) ・・・・・ (0.0, 0.0, 0.0)
数値が変化しながら最後は0で終わります。
アニメーションも意図した通りに動いてくれています。

情報が不足していたら申し訳ございません。
よろしくお願いいたします。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

経路探索が完了しない内にwhileに入ってしまい、そのまま抜けているのではないでしょうか。
Unity - スクリプトリファレンス: NavMeshAgent.SetDestination
上記ページを確認の上、agent.pathPending が false になるのを待ってから以後の処理を開始してみてください。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/09/12 10:46 編集

    早速のご返信ありがとうございます。
    以下のように変更してみたのですが、やはり初回だけ0になってしまいます。
    やり方、間違ってますでしょうか?

    ```C#
    //移動開始
    bool request = agent.SetDestination (hit_info.point);
    if (request) {
    StartCoroutine (Walk ());
    }
    ```

    ```C#
    IEnumerator Walk ()
    {
    animator.SetBool ("attack", false);
    animator.SetBool ("stop", false);

    yield return !agent.pathPending;

    Debug.Log (agent.pathPending);

    while (true) {
    Debug.Log (agent.velocity);

    if (agent.velocity.z > 0.5f) {
    animator.SetInteger ("direction", 1);
    } else if (agent.velocity.z < -0.5f) {
    animator.SetInteger ("direction", 2);
    } else if (agent.velocity.x > 0.5f) {
    animator.SetInteger ("direction", 3);
    } else if (agent.velocity.x < -0.5f) {
    animator.SetInteger ("direction", 4);
    }
    now_direction = animator.GetInteger ("direction");

    if (agent.velocity == Vector3.zero) {
    animator.SetBool ("stop", true);
    yield break;
    }

    yield return null;
    }
    }
    ```

    出力結果
    ------------ 初回
    True
    False
    (0.0, 0.0, 0.0)

    ------------- 2回目
    True
    False
    (0.9, 0.0, 0.3) .... (0.0, 0.0, 0.0)

    コメント欄でのコードの整形の仕方がわからなくて、見づらくなってしまい申し訳ございません。

    キャンセル

  • 2017/09/12 10:48

    リファレンス読み間違えてました。
    「経路探索中はagent.pathPending が true」なので、「falseになるまで待つ」が正しいです。

    それと
    yield return !agent.pathPending;
    この部分を

    while (agent.pathPending) {
    yield return new WaitForEndOfFrame();
    }

    に置き換えてみてください。
    (ただDebug.Logが同じ物を返しているのでこれが原因ではない可能性もありますが)

    キャンセル

  • 2017/09/12 11:07 編集

    お返事ありがとうございます。
    リファレンス、私も最初読み間違えました。ややこしいですね。

    ご指摘の通りに書き換えてみましたが、結果は同じでした。

    while (agent.pathPending) {
    yield return new WaitForEndOfFrame ();
    }

    Debug.Log (agent.pathPending);

    Debug.Log (agent.pathPending)でTrueが返されなくなりました。
    velocityの出力値は0のままです。
    どうやら一瞬だけアニメーションが表示されたり・されなかったりしているみたいです。
    移動はしてます。

    キャンセル

  • 2017/09/12 11:13

    Unity - スクリプトリファレンス: NavMeshAgent.velocity
    https://docs.unity3d.com/ja/540/ScriptReference/NavMeshAgent-velocity.html
    上記を見ると「値を設定する場合、次の更新で効果が現れます。」とありますので、
    速度取得についても1フレームかかるのかもしれません。

    while (agent.pathPending) {
    yield return new WaitForEndOfFrame ();
    }
    yield return new WaitForEndOfFrame ();

    こんな感じでどんな場合でも1フレーム待ってみてください。
    これでダメなら強硬手段としてboolで管理ですかね……一見よく分からないコードになりますが……

    bool isMoveStart = false;

    while(true) {
    〜中略〜

    if (agent.velocity != Vector3.zero) {
    isMoveStart = true;
    }

    if (agent.velocity == Vector3.zero && isMoveStart) {
    animator.SetBool ("stop", true);
    yield break;
    }
    }

    アニメーションが表示されるのは、velocityが0のままでも1回は animator.SetBool ("stop", true); が呼ばれるからではないでしょうか?

    キャンセル

  • 2017/09/12 11:21

    待つ方法で、できました!
    velocityもちゃんと値が入ってます!
    処理に時間がかかるから待ってあげれば良かったんですね。
    すごく勉強になりました。自力では無理でした。
    本当にありがとうございました。

    while (agent.pathPending) {
    yield return new WaitForEndOfFrame ();
    }
    yield return new WaitForEndOfFrame ();

    Debug.Log (agent.pathPending);

    animator.SetBool ("attack", false);
    animator.SetBool ("stop", false);

    キャンセル

  • 2017/09/12 11:33

    解決して良かったです。

    ちょっとだけ補足すると、最後に追記したWaitForEndOfFrameについては、
    「処理に時間がかかるから」待つのではなく、
    「まだ実行(反映)されていないから」待つ、というイメージです。

    私も正確には把握していないので「そういうものなんだ」という認識ですが、コルーチンの時間(フレーム)の関係を確認すると後学になるかもしれません。

    キャンセル

  • 2017/09/12 11:41

    補足までありがとうございます!
    まだyied returnもよく理解できていなくて、整理していたところでした。
    反映されていないから待つ、なのですね。
    理解できるかどうかわかりませんが、じっくり勉強してみたいと思います。
    ありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.99%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 解決済

    [初心者]UnityでFPSを作っています。

    前提・実現したいこと unityでFPSを作っています、今銃から弾を発射するscriptで悩んでいます。 発生している問題・エラーメッセージ scriptを作成後bull

  • 解決済

    UnityのRayについて

    前提・実現したいこと Unityにて敵からプレイヤーに対してRayを飛ばしプレイヤーまでの間に壁などの障害物がないかを確認したいです しかしプレイヤーに対してRayを飛ばしてい

  • 解決済

    Nav Mesh Agentの切り替え方法

    以下の画像のような状況で,手前の赤四角をクリックすると赤キューブが動かせて,青四角をクリックすると青キューブが動かせるようにしたいです。 赤キューブと青キューブは画面

  • 受付中

    アニメーションの状態を取得したいのですが

    すみません、うまく伝えられないのですが質問させてください。 アニメーションの状態を取得したいのですが、どのようなスクリプトを書けば良いでしょうか? 具体的には、 if

  • 解決済

    ​ uGUIからRayを飛ばす

    ​ マウス(タッチ)でオブジェクトを取得するにあたって Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition

  • 解決済

    (Unity)他のクラス(スクリプト)から呼び出したサウンド(メソッド)を1回だけ鳴らす方法

    Unity(C#)でVRのゲームを作っています。 特にエラー等は出ていないのでアップデート関数より上とInvokeで呼び出しているGameStartメソッドは割愛しております。

  • 受付中

    Unity C# でオブジェクトを移動させたい。

    前提・実現したいこと 今、Unityで横スクロール型の2Dゲームを作っています。 ゲームの内容は、Enterを押してゲームスタート、キャラクターがゴールまで自動で走り、 シフトを押

  • 解決済

    unity 5.6: なぞったところをオブジェクト化したいです

    画面をなぞった線の部分をオブジェクト化したいです。イメージ的には"Q universe"のようなかんじです。 GameObject gObj = null; Ray

同じタグがついた質問を見る

  • Unity

    3036questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity2D

    555questions