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

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

新規登録して質問してみよう
ただいま回答率
85.48%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

2438閲覧

unityでbouncinessを1にすると1+αの力で跳ね返るのはなぜか

G_chan

総合スコア17

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

1クリップ

投稿2020/01/13 07:26

お世話になります。

表題の通りなのですが、PhysicsMaterialのbouncinessを1にするとエネルギーの損失なし+αの力で跳ね返ってくるのはどうしてでしょうか。

シミュレーションするにあたって計算負荷をかけないように都合のいい値にするために少量のエネルギーが追加されたのでは?と推測したのですが、bouncinessの値が1はそもそも都合のいい値であると思うので追加する必要がないのではと考えております。

そもそもこの推測が間違っていると思っていますが、ご回答いただければ幸いです。

使用しているUnityのバージョンは2018.4.13f1です。

よろしくお願いいたします。

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

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

sakura_hana

2020/01/14 01:26

直接的な原因は不明ですが、「1+αの力」というのをどう確認したのか書いて頂けると何かとっかかりになるかもしれません。
G_chan

2020/01/15 03:35

オブジェクトの位置を取得しました。落下して跳ね返ってきた位置が落下前の位置より高くなっていました。 また、連続して跳ね返りを続けると徐々に位置が高くなっていきます。
guest

回答1

0

ベストアンサー

確かに高所からボールを落としてみると、最初の高さよりも少しだけ高くまで上がってしまいますね。気になって調べてみたものの、結局回答としては「わかりませんでした」ということになって申しわけないです...

How to get a perfect bouncing ball. - Unity Answersでも似たような話題が上がっており、ご質問者さんがおっしゃるようにBouncinessが1だと衝突するたびに少しずつエネルギーが増大してしまう現象が報告されていました。
寄せられた意見の中にもあるように、物理シミュレーションの計算過程は公開されていないため、致し方ない誤差なのか修正可能なバグなのか、あるいはご質問者さんがおっしゃるように他の問題を回避するための妥協なのかはよくわからないようです。

Collision DetectionをContinuousにすると改善するとの回答もありましたが、私の試したかぎりではいまいち有効でなく、やはり元の高さよりも高くまで跳ね返ってしまいました。その他の対策としてBouncinessを1よりほんの少しだけ小さくするという案も出ていますが、ある程度長時間バウンドさせ続けたい場合は微小なズレが蓄積してしまい、だんだん目に見えて高さが変わってきてしまいそうな気がします。

どうしても完全弾性衝突でないと困る部分だけに限っては、物理シミュレーションの結果を理想的な値に書き換えてしまうのがいいんじゃないでしょうかね?
たとえば余計な外力がかからず、回転エネルギーの分配も考慮せず、衝突相手は運動量を持たず、重力によって跳ね続けるだけのボール...のような限定的なシチュエーションなら、一例として下記のように速度を無理やり矯正してしまうというのはどうでしょう。

C#

1using UnityEngine; 2 3public class VelocityCorrector : MonoBehaviour 4{ 5 private new Rigidbody rigidbody; 6 private Vector3 previousVelocity; 7 8 private void Awake() 9 { 10 this.rigidbody = this.GetComponent<Rigidbody>(); 11 12 // 初速度をセットする 13 // たとえば初期位置からさらに10m上まで飛ばす場合... 14 this.rigidbody.velocity = -Physics.gravity.normalized * Mathf.Sqrt(10.0f * 2.0f * Physics.gravity.magnitude); 15 } 16 17 private void FixedUpdate() 18 { 19 // FixedUpdate時点の速度を常にキャッシュしておき... 20 this.previousVelocity = this.rigidbody.velocity; 21 } 22 23 private void OnCollisionEnter(Collision _) 24 { 25 // 衝突直後において、速度の大きさは衝突直前よりも大きくなっているので 26 // 衝突直前の速度と同じ大きさにむりやり矯正する 27 this.rigidbody.velocity = this.previousVelocity.magnitude * this.rigidbody.velocity.normalized; 28 } 29}

数分間観察したかぎりではいい感じにほぼ同じ高さまで跳ね続けているようでしたが、エネルギーの保存を明示的に組み込んでいるわけではないので、もっと長時間だと高さが変わってしまう可能性があります。
試しにゲーム開始時点の力学的エネルギーを超えないことを重視したバージョンも作ってみたものの、上記のバージョンよりもだいぶややこしくなってしまいました。

C#

1using UnityEngine; 2 3public class EnergyCorrector : MonoBehaviour 4{ 5 private new Rigidbody rigidbody; 6 private Vector3 initialPosition; 7 private Vector3 previousPosition; 8 private float mechanicalEnergy; 9 private bool hit; 10 11 private void Awake() 12 { 13 this.rigidbody = this.GetComponent<Rigidbody>(); 14 15 // ゲーム開始時点の位置を覚えておき、ここをポテンシャル0の位置とする 16 this.initialPosition = this.rigidbody.position; 17 18 // 初速度はこのタイミングでセットする 19 // たとえば初期位置からさらに10m上まで飛ばす場合... 20 this.rigidbody.velocity = -Physics.gravity.normalized * Mathf.Sqrt(10.0f * 2.0f * Physics.gravity.magnitude); 21 22 // この時点の力学的エネルギーは運動エネルギーのみとする 23 // 質量は変化しないと仮定して省略 24 this.mechanicalEnergy = this.rigidbody.velocity.sqrMagnitude * 0.5f; 25 26 // 初速度をもとに、FixedUpdate内で使うpreviousPositionの初期値を決める 27 this.previousPosition = this.initialPosition - (this.rigidbody.velocity * Time.fixedDeltaTime); 28 } 29 30 private void OnCollisionEnter(Collision _) 31 { 32 this.hit = true; 33 } 34 35 private void FixedUpdate() 36 { 37 // 初期位置に対する現在の位置のずれによって位置エネルギーが決定される 38 // 質量は変化しないと仮定して省略 39 var currentPosition = this.rigidbody.position; 40 var potentialEnergy = Vector3.Dot(Physics.gravity, this.initialPosition - currentPosition); 41 42 // 力学的エネルギーから位置エネルギーを除いた残りが運動エネルギーということに 43 // なるので、それをもとに期待される速度の大きさの二乗が得られる 44 // もし力学的エネルギーの上限を超えた高さまで上がると運動エネルギーが負になって 45 // しまうが、その時はむりやり0に書き換えてエラーを防ぐ 46 var kineticEnergy = this.mechanicalEnergy - potentialEnergy; 47 var expectedSqrSpeed = Mathf.Max(kineticEnergy * 2.0f, 0.0f); 48 49 // 速度の向きはrigidbody.velocityはあてにせず、前回の位置と今回の位置の差から決める 50 // (ジャンプの最高点付近で止まってしまう現象を回避するための措置...rigidbody.velocityを 51 // 速度の向きとして使用した場合、おそらく「上昇する向きに修正後の速度を持たせる」→「シミュレーションに 52 // よって下向きに加速し速度が小さくなるがまだ上向き」→「更新された速度をもとに位置を計算した結果 53 // 元の位置と同じ位置になる」→「次のFixedUpdateで取得される速度はまだ上向きなので、結局また 54 // 同じ状況を繰り返し上昇も下降もできずに固まってしまう」といった状態なのではないだろうか?) 55 // ただし、直前に衝突があった場合はrigidbody.velocityを使用する 56 // その場合も、rigidbody.velocityの大きさはすでに理想値よりも大きくなっておりあてにならないので 57 // 向きだけを参考にする 58 // (垂直に跳ねるだけなら問題は顕在化しにくいかもしれないが、位置だけを頼りにしたのでは 59 // 前回から今回にかけて何らかの衝突があった場合に正しい向きにならない可能性があるかもと 60 // 思っての措置) 61 // なお、ベクトルの大きさが極めてわずかだとnormalizedの結果がゼロベクトルになるが、 62 // その場合は重力の向きを採用することにして、常に何かしらの向きを持つようにする 63 var velocityDirection = (this.hit ? this.rigidbody.velocity : currentPosition - this.previousPosition).normalized; 64 if (velocityDirection.sqrMagnitude < 0.5f) 65 { 66 velocityDirection = Physics.gravity.normalized; 67 } 68 this.hit = false; 69 this.previousPosition = currentPosition; 70 71 // 速度を期待される大きさに修正する 72 this.rigidbody.velocity = velocityDirection * Mathf.Sqrt(expectedSqrSpeed); 73 } 74}

開始直後
対策なしだと数回の跳ね返りで目に見えて最高高度が上がっています。

図1

およそ1時間後
VelocityCorrectorは誤差が蓄積して元の高さよりもやや高くまで上がるようになりましたが、EnergyCorrectorは20mをキープできました。対策なしはかなり高くまで跳ねるようになり、なかなか落ちてきません。

図2

投稿2020/01/14 13:35

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

G_chan

2020/01/17 03:28

回答ありがとうございます。 Bongoさんがおっしゃるように非常に限定的なシチュエーションです。 そのため、開発上で困っていたわけではなく軽い気持ちで質問したにもかかわらず詳細な回答と対策までご教授いただき感謝しております。 おかげさまで疑問が解消されました。 未熟者故、またご質問させていただくことがあると思いますがよろしくお願いいたします
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問