69014


Unityで移動する処理を作っています。  
オブジェクトが指定位置に移動する処理なのですが、表題の通り、ただ移動するだけではなく、このようなあらかじめ定められた距離に対して、速度が減衰して、到達する処理を求めたいと思っております。  
初速が大きく、徐々に減衰して、指定時間後に指定距離移動する処理がうまく求められないでいます。  
具体的には、添付gifの初速度=10のときに、折り返さないで位置に到達するようにしたいです。  
等加速度直線運動の公式から、s=v0t＋1/2at^2を利用して、加速度aを求めましたが、それだけではだめなようです。

ご教授いただければ幸いです。

[code]

    public class Test : MonoBehaviour
    {
        public float t;
        public float initSpd;
        public Transform t1, t2;
    
        IEnumerator Start()
        {
            var p = t1.position;
            while (true) {
                t1.position = p;
                var v0t = initSpd * t;
                var distance = Vector2.Distance(t1.position, t2.position);
                var direction = (t2.position - t1.position).normalized;
                var a = (2 * (distance - v0t)) / (t * t);
                var v = initSpd;
                // 指定秒数間、繰り返し
                yield return Utils.Coroutine.WhileForSeconds(t, () =>
                {
                    t1.Translate(direction * v * Time.deltaTime);
                    v += a * Time.deltaTime;
                });
            }
        }
    }
    
[/code]

時間=3, 初速度=0  
[![初速度=0](https://i.stack.imgur.com/RDtgm.gif)](https://i.stack.imgur.com/RDtgm.gif)  
時間=3, 初速度=3  
[![画像の説明をここに入力](https://i.stack.imgur.com/Mq8BF.gif)](https://i.stack.imgur.com/Mq8BF.gif)  
時間=3, 初速度=10  
[![画像の説明をここに入力](https://i.stack.imgur.com/hOR66.gif)](https://i.stack.imgur.com/hOR66.gif)

追記：ボールはこのようにカーブすることもあります。  
赤い線が距離になります。適切なマイナス加速度"a"が求められれば、あとは毎フレーム方向を変えるだけと考えておりました。達成したいことは、指定した時間に、減衰していく速度で、ランダムな方向に移動し、初期の2点間の距離分移動したいです。

追記：  
[![画像の説明をここに入力](https://i.stack.imgur.com/J24wl.jpg)](https://i.stack.imgur.com/J24wl.jpg)

追記:  
速度を2段階変化させることで理想の挙動を得ることができました。  
BA様ありがとうございました。

[code]

        IEnumerator Start()
        {
            var p = t1.position;
            while (true)
            {
                t1.position = p;
                var direction = (t2.position - t1.position).normalized;
                var distance = Vector2.Distance(t1.position, t2.position);
    
                var v0 = initSpd;
                var M = distance;
                var T = duration;
                var tm = M / v0;
                var vm = v0 / T * tm;
                var accel1 = (vm - v0) / (tm - 0);
                var accel2 = (0 - vm) / (T - tm);
                Debug.Log($"vo={v0}, M={M}, T={T}, tm={tm}, vm={vm}, accel1={accel1}, accel2={accel2}");
                var v = initSpd;
                var stime = Time.time;
                var hist = 0f;
                // update
                yield return Utils.Coroutine.WhileForSeconds(T, () =>
                {
                    t1.Translate(direction * v * Time.deltaTime);
                    hist += v * Time.deltaTime;
                    if (Time.time - stime <= tm)
                        v += accel1 * Time.deltaTime;
                    else
                        v += accel2 * Time.deltaTime;
                });
                Debug.Log($"経過時間={Time.time - stime}, 移動距離={hist}, v={v}");
            }
        }
    
[/code]


CC BY-SA 4.0
2020-07-25T04:38:27.360
68931
2020-07-29T12:18:18.200
2020-07-29T12:18:18.200
96
96
question
c# unity3d
指定秒数で指定距離に、加速度がマイナスの等加速度直線運動のような移動を求めたい


ボールを真上に放り投げる場合のように、初速のベクトルと、重力加速度のベクトルが、完全に逆向きの場合は、指定の距離を進んだ(ボールが上がった)ところで速度が０になるというのは可能です。

逆風に向かって摩擦のない面上で物体を打ち出す場合だと、所定の距離を進んだところで速度が０にできると思います（ただし、停止した後、逆風によって戻ってきます）

重力がある場で、斜めに物体を放り投げる場合、空気抵抗を無視すれば、初速と同じ速度で物体が地面に衝突します。  
このようなケースでも、指定の距離をすすんだところで速度を０にするには、粘性抵抗など、何らかの速度低減要因を仮定する必要があると思います。


CC BY-SA 4.0
2020-07-25T15:03:51.283
68959
2020-07-25T15:03:51.283
217
68931
answer


等加速度運動では停止するまでの距離、経過時間、初速を同時に指定できません。  
「等加速度運動でT秒後に止まる」を逆に言えば、  
「止まった状態から等加速度運動でT秒経ったら速度vは？」となるからです。

初速v0を指定しつつ距離Mを指定しT秒後に静止する、を実現するには  
例えば以下のように加速度を2段階にわけます。

xyグラフ = t[s]v[m/s]グラフにおいて、  
p1(t,v)=(0,v0)  
p2(t,v)=(T, 0)  
p3(t,v)=(T,V0)  
pm(t,v)=(tm, vm)原点Oからp3を結ぶ線分上のどこか  
xy軸と線分p1pm、線分pmp2で囲まれた面積をMとする  
あとは連立式からpm(tm, vm)を求めればOK

[code]

      v[m/s]
      A
      |
    v0*-   -   -   -   + p3
    vm|             *pm
      |　　　　　       |
      |       M[m]
      |                |
      +----------------*------> t[s]
     O              tm T
    
    M = (1/2*tm*(v0-vm)) + tm*vm + (1/2*(T-tm)*vm)
    　　※補足
    　　　距離 = 速さ * 時間より、tvグラフは面積＝距離になります
         Mは、直角三角形v0vmpm、直角三角形pmtmT、四角形Ovmpmtmの合計になる
    
    vm = v0/T * tm
    　　※補足
    　　　原点Oと点p3を通る比例グラフの傾きは、v0/T
    
    上記2式より
    tm = M/v0
    　　※補足
    　　　すいません、計算ミスしてました。常に上記になるようです…
    
    ※ その他補足
    　　tvグラフにおいて、グラフの傾きは加速度です。
    　　直線ならば等加速度運動。
    　　今回の場合、v0～pm、pm～Tと折れ線にすることで、2段階の加速度になっています。
    
[/code]


CC BY-SA 4.0
2020-07-27T06:37:55.760
69014
2020-07-29T11:15:51.510
2020-07-29T11:15:51.510
25396
25396
68931
answer


コメントで書いてしまいましたが、回答の方に書いておきます。

実現イメージとして、sを求める公式を使い、現時点での期待される移動量(距離)から、前回の地点を差し引いた分を、今回移動させることかと思います。  
Unityは触ってないですが、こんな感じでしょうか…？

[code]

    t1.Translate(
      // 単位ベクトル
      direction *
      // sを求める式。経過時間Time.timeの時点での移動量を求める
      (initSpd * Time.time) + 1 / 2.0 * a * (Time.time * Time.time) -
      // 前回の位置までは移動済みなのでその分を差し引いて、今回のフレームの相対移動量にする
      (t1.position - p));
    
[/code]

元のコードの問題点は、たとえばdeltaTimeが30msの場合、「30msの間同じ速度で移動してしまう」ことです。  
等加速運動は、その30msの間も「速度を変化させながら移動する」こと（連続的な現象）ですので、そのズレの分、移動しすぎる（突き抜ける）現象が出ているのだと思います。


CC BY-SA 4.0
2020-07-27T13:03:40.027
69023
2020-07-27T13:03:40.027
4684
68931
answer