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

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

ただいまの
回答率

90.32%

  • Unity

    4432questions

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

velocityでの次の到達地点

解決済

回答 1

投稿 編集

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

tkmnusr

score 180

 前提・実現したいこと

transform系での移動は、Mathf.Clampを使えば移動制限できるみたいですが、
velocityでの移動はどのように制御すれば移動制限できるのでしょうか?
ご教示お願いします。

 該当のソースコード

    void FixedUpdate(){
        rb.velocity = this.transform.right * speed;
    }

 試したこと

transform系での移動は、こちらのサイトのように、
Mathf.Clampを使えば移動制限できることがわかりました。

    // 移動量を加える
    pos += direction * spaceship.speed * Time.deltaTime;

    // プレイヤーの位置が画面内に収まるように制限をかける
    pos.x = Mathf.Clamp (pos.x, min.x, max.x);
    pos.y = Mathf.Clamp (pos.y, min.y, max.y);

velocityの場合でも、Mathf.Clampを使って移動制限できるのでしょうか?
可能であれば、Mathf.Clampを使った移動制限を知りたいですが、
不可能であれば、velocityでの次の到達点が指定範囲内ならば、
移動するといった制御にしたいです。

    void FixedUpdate(){
        if(velocityでの次の到達点<max.x){
            rb.velocity = this.transform.right * speed;
        }
    }

妥協案として、「移動前に制限」ではなく、「移動後に調整して制限」を考えてみたのですが、
下記のコードでも、移動制限されませんでした。
下記コードが意図通りにいかない理由もわかりませんが、できればこのようなコードではなく、
「移動前に制限」させたいです。

    void FixedUpdate(){        
        rb.velocity = this.transform.right * speed;
        if(this.transform.position.x > max.x){
            Vector3 limit = new Vector3(max.x, this.transform.position.y, this.transform.position.z);
            this.transform.position = limit;
        }
    }

 追記。

    void Update () {
        Vector3 viewport = Camera.main.WorldToViewportPoint(this.transform.position);
        Vector3 viewmin = new Vector3(0.0f, 0.0f,viewport.z);
        Vector3 viewmax = new Vector3(1.0f, 1.0f,viewport.z);
        min = Camera.main.ViewportToWorldPoint(viewmin);
        max = Camera.main.ViewportToWorldPoint(viewmax);

    }

    void FixedUpdate(){

        Vector3 pos = this.transform.position + (this.transform.right * speed);
        pos.x = Mathf.Clamp(pos.x, min.x, max.x);

        rb.velocity = pos - this.transform.position; 

 追記②

float speed = 2.0f;
Vector3 min;
Vector3 max;

void Update () {
    Vector3 viewport = Camera.main.WorldToViewportPoint(this.transform.position);
    Vector3 viewmin = new Vector3(0.0f, 0.0f,viewport.z);
    Vector3 viewmax = new Vector3(1.0f, 1.0f,viewport.z);
    min = Camera.main.ViewportToWorldPoint(viewmin);
    max = Camera.main.ViewportToWorldPoint(viewmax);
    Debug.Log(max);
}

void FixedUpdate(){

    Vector3 pos = this.transform.position + this.transform.TransformDirection(this.transform.right * speed);
    pos.x = Mathf.Clamp(pos.x, min.x, max.x);

    rb.velocity = transform.InverseTransformDirection(pos - this.transform.position); 
}

 追記③

    Vector3 max;

    void Start () {
        //空のゲームオブジェクトの位置に設定。
        max = go.transform.position;
    }

    void FixedUpdate(){

        Vector3 pos = this.transform.position + this.transform.TransformDirection(this.transform.right * speed);
        pos.x = Mathf.Clamp(pos.x, min.x, max.x);

        rb.velocity = transform.InverseTransformDirection(pos - this.transform.position); 
    }

イメージ説明

イメージ説明

 追記④

イメージ説明

    Vector3 min;
    Vector3 max;

    void Start () {
    }


    void Update () {
        Vector3 viewport = Camera.main.WorldToViewportPoint(this.transform.position);
        Vector3 viewmin = new Vector3(0.0f, 0.0f,viewport.z);
        Vector3 viewmax = new Vector3(1.0f, 1.0f,viewport.z);
        min = Camera.main.ViewportToWorldPoint(viewmin);
        max = Camera.main.ViewportToWorldPoint(viewmax);
    }


    void FixedUpdate(){

        Vector3 pos = this.transform.position + this.transform.TransformDirection(this.transform.right * speed);
        pos.x = Mathf.Clamp(pos.x, min.x, max.x);
        pos.z = Mathf.Clamp(pos.z, min.z, max.z);

        rb.velocity = transform.InverseTransformDirection(pos - this.transform.position); 

 追記⑤

    void Update () {
        Vector3 viewport = Camera.main.WorldToViewportPoint(this.transform.position);
        Vector3 viewmin = new Vector3(0.0f, 0.0f,viewport.z);
        Vector3 viewmax = new Vector3(1.0f, 1.0f,viewport.z);
        min = Camera.main.ViewportToWorldPoint(viewmin);
        max = Camera.main.ViewportToWorldPoint(viewmax);
    }


    void FixedUpdate(){

        Vector3 baseVelocity = this.transform.right * speed;
        Vector3 pos = this.transform.position + this.transform.TransformDirection(baseVelocity);
        pos.x = Mathf.Clamp(pos.x, min.x, max.x);

        Vector3 vel = transform.InverseTransformDirection(pos - this.transform.position);
        vel.z = vel.z * vel.x /baseVelocity.x;
        rb.velocity = vel;
    }

下の画像の位置で停止しました。ちょっと画面外に出てしまっています。
※移動時はy方向に動きません。
イメージ説明

 追記⑥

追記⑤の画像の停止位置とほぼ変わらない状態でした。

    void FixedUpdate(){

        Vector3 viewport = Camera.main.WorldToViewportPoint(this.transform.position);
        Vector3 viewmin = new Vector3(0.0f, 0.0f,viewport.z);
        Vector3 viewmax = new Vector3(1.0f, 1.0f,viewport.z);
        min = Camera.main.ViewportToWorldPoint(viewmin);
        max = Camera.main.ViewportToWorldPoint(viewmax);

        Vector3 baseVelocity = this.transform.right * speed;
        Vector3 pos = this.transform.position + this.transform.TransformDirection(baseVelocity);
        pos.x = Mathf.Clamp(pos.x, min.x, max.x);

        Vector3 vel = transform.InverseTransformDirection(pos - this.transform.position);
        vel.z = vel.z * vel.x /baseVelocity.x;
        rb.velocity = vel;
    }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

0

「速度」とは「時間あたりの移動量」です。
なので「現在地+velocity」が次の移動地点です。

よってMathf.Clampを使う場合もTransform系の移動と同じように出来ます。

    // 入力後の移動量を求める
    pos = this.transform.position + this.transform.right * speed;

    // プレイヤーの位置が画面内に収まるように制限をかける
    pos.x = Mathf.Clamp (pos.x, min.x, max.x);
    pos.y = Mathf.Clamp (pos.y, min.y, max.y);

    // 速度に反映
    rb.velocity = pos - this.transform.position;

妥協案のコードが上手くいかないのは、
「そのフレームでの位置」は決定されていても、「速度」がthis.transform.right * speedのままキープされているので、次のフレームでは移動が再開されるからだと思います。
例えるならエンジンがかかりっぱなしの車をある一点に置いても、次の瞬間には走り出して行ってしまうようなものです。
下記のようにすれば上手くいくのではないかと思います。

void FixedUpdate(){        
        if(this.transform.position.x > max.x){
            Vector3 limit = new Vector3(max.x, this.transform.position.y, this.transform.position.z);
            this.transform.position = limit;
            rb.velocity = Vector3.zero;
        } else {
            rb.velocity = this.transform.right * speed;
        }
    }

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/06/17 17:07

    ご回答ありがとうございます。
    ご回答者様の2つのコードを試してみたのですが、どちらのコードも移動が停止しなくてわからなかったのですが、動かすゲームオブジェクトの三軸がワールド空間の三軸と同じ向きでないことが原因であることがわかりました。
    ゲームオブジェクトの三軸をワールドの三軸と同じ向きにすると、2つのコードそれぞれで意図通り停止することが確認できましたが、ゲームオブジェクトの三軸の向きがワールドの三軸と合わない場合は、どのようにコードを書けばよいか教えていただけませんか?

    キャンセル

  • 2018/06/17 17:16

    具体的にどうずれてて、何がしたいのかによりますが、
    「ゲームオブジェクトのXY」と解釈して制限の入力値側を調整するか(本来Yに入る値をXに入れるとか)、
    調整の計算式を変更する(pos.x = Mathf.Clamp (pos.x, min.y, max.y);みたいな)か、
    this.transform.positionの代わりにthis.transform.localPositionを使うとかですかね?

    キャンセル

  • 2018/06/17 18:19 編集

    ご回答ありがとうございます。説明不足ですみません、追記しました。
    目的は画面外に行かせないようにしたいのですが、ViewportToWorldPointがワールドでなく、オブジェクトのローカルな座標で取得できれば実現できるように思えるのですが、そういった方法はありませんか?
    (ローカルな座標で取得できて、ローカルな位置移動すれば実現できるという前提が合っていればのお話ですが。)
    もしくは何か方法があればご教示いただけませんか?

    キャンセル

  • 2018/06/18 11:02

    Vector3 pos = this.transform.position + transform.TransformDirection(this.transform.right * speed);
    pos.x = Mathf.Clamp(pos.x, min.x, max.x);
    rb.velocity = transform.InverseTransformDirection(pos - this.transform.position);

    これだとどうでしょうか。
    TransformDirection:ローカル軸からワールド軸へ方向を変換
    InverseTransformDirection:ワールド軸からローカル軸へ方向を変換

    キャンセル

  • 2018/06/19 00:25 編集

    ご回答ありがとうございます。追記しました。
    検証してみたのですが、移動が画面外まで続いてしまいました。
    メインカメラが動き回ることにも対応させる為、Updateでminやmaxをとっているんですが、
    ゲームを実行してメインカメラを固定させていても、Update内でDebug.Log(max)したら、
    値が変わり続けていました。
    このmaxが変動するのがおかしい為に、移動制限ができていないということでしょうか?

    キャンセル

  • 2018/06/19 10:14

    >このmaxが変動するのがおかしい為に、移動制限ができていないということでしょうか?
    そうなります。
    試しにviewport.zの部分を固定値にするとどうなるかとか(恐らく変わらないように思いますが)、
    いっそmaxをViewportToWorldPointから取得しない完全固定値にするとどうなるか確認してみてください。

    キャンセル

  • 2018/06/19 20:34

    ご回答ありがとうございます。
    追記③を追記しました。
    maxを空のゲームオブジェクトの位置の固定値に設定したのですが、
    空のゲームオブジェクトを通り過ぎても、少し軌道を変えて移動を続けました。

    キャンセル

  • 2018/06/20 10:53

    制限しているのはXだけなので、YとZ方向に移動し続けるのは当然ですね。
    (this.transform.rightのY値が0なので結果的にY軸の移動はしていませんが、制限無し状態ではある)

    キャンセル

  • 2018/06/21 00:36

    ご回答ありがとうございます。
    空のゲームオブジェクトの検証では、Z方向も制限することで停止することが確認できました。
    すみません、画面外の制限が説明不足でしたので、追記④を追記しました。
    画面外の制限の場合、右方向に進むたびにZの値も変わるので、
    posのZ値は更新を続けるviewport.zのminとmaxで制限すべきなのかと思って試してみたのですが、意図しない動きになってしまいました。
    どのようにすれば画面外制限が実現できますか?
    何度もすみません。

    キャンセル

  • 2018/06/21 10:13

    なるほど、実際にZ方向にも動くんですね。

    ViewportToWorldPointは「Z地点に存在する平面」を基準としてワールド座標を取得します。
    なのでZ値の制限は出来ません。(Z値を基準として範囲を決定しているので、そのZ値が動いたら基準となる範囲も変わる)

    とは言えX値が制限に達した時にZ値も移動不可となる訳ですから、ここは比例関係になります。

    Vector3 baseVelocity = this.transform.right * speed; //ベースとなる速度
    Vector3 pos = this.transform.position + this.transform.TransformDirection(baseVelocity);
    pos.x = Mathf.Clamp(pos.x, min.x, max.x);
    //一旦速度を算出
    Vector3 vel = transform.InverseTransformDirection(pos - this.transform.position);
    vel.z = vel.z * pos.x / baseVelocity.x;
    rb.velocity = vel;

    元の速度X値とClamp後のX値の割合を算出。元の速度Z値にその割合を反映させています。
    精査出来ていませんので適宜綺麗にしてもらえればと思います。(Y値も動くなら上手いこと調整は必要かと)

    ただし、カメラから離れれば離れる程見えていると判定される範囲は広がりますので、カメラ位置固定の場合は画面端で微妙に動き続けると思います。
    Z値の範囲を定数で定めてしまうか、カメラがターゲットを(Z方向だけでも)追いかけるようにする必要があるかと思います。

    キャンセル

  • 2018/06/22 00:16

    ご回答ありがとうございます。
    なるほど、比例関係で表せるのですね。
    しかし、「vel.z = vel.z * pos.x / baseVelocity.x;」で実行した所、よくわからない移動を続けたので(画面外に出てからカメラに近づいて一旦停止後、またカメラから離れる方向に動き続ける)
    試しに「vel.z = vel.z * vel.x /baseVelocity.x;」と書いた所、割とそれっぽい動きをしたのですが、
    停止位置が画面外よりややオーバーしてしまいました(追記⑤の画像です)。
    このコードの修正が合っているかどうかも自信がないのですが、画面外にけっこう出てしまうのは、これ以上修正の効かない誤差なのでしょうか?

    キャンセル

  • 2018/06/22 10:41

    計算式はご指摘の通りです(こちらが間違えてました)。

    「正常に動作はしているが誤差っぽい」という結果なので、
    現在Updateに入っている計算式を全部FixedUpdateに入れるとどうでしょうか。
    (50fps以上出ているならあまり変わらないとは思いますが)

    キャンセル

  • 2018/06/22 20:29

    ご回答ありがとうございます。
    Update内の処理もFixedUpdateにまとめてみたのですが、結果はあまり変わらなかったです。
    ただ、maxのviewportを0.8f辺りに設定することで画面境界付近で停止してくれました。
    無理矢理な対処法にはなってしまいますが、これよりもキレイなコードで修正できる方法ってないですよね?

    キャンセル

  • 2018/06/25 15:43

    私もZ値が動くケースはやったことが無いので、近似値が出せたならそれで収めた方がいいかもしれないです。
    綺麗にまとめられずすみません。

    キャンセル

  • 2018/06/26 00:33 編集

    ご回答ありがとうございます。
    いえいえ、とんでもないです。
    ご教示くださり、ありがとうございました。

    キャンセル

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

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

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

  • Unity

    4432questions

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