teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

1

めり込み現象について追記

2018/06/11 18:56

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -58,4 +58,101 @@
58
58
  }
59
59
  ```
60
60
 
61
- ![プレビュー](918bea2c313131d381226f0ef49236bf.gif)
61
+ ![プレビュー](918bea2c313131d381226f0ef49236bf.gif)
62
+
63
+ ### めり込み現象について追記
64
+ パックを静かにフィールド隅に押しつけてみたところ、確かにパドルがパック内にめり込んでいきますね。
65
+ (確認しやすくするため、パックを大きくしています)
66
+
67
+ ![めり込み](2d6cb6a95ba39fcf897b85001455a299.png)
68
+
69
+ フィールド壁面はStaticなので物理的作用を受けず、パドルもキネマティックなため物理的作用を受けないということになるでしょう。動くことができるのはパックだけという状況で力の釣り合いを解決した結果、上図のようなめり込んだ状態に落ち着いたのだろうと思います。
70
+
71
+ パックを無理やり壁面に押しつけようとした場合、感覚的にはパドルが押し返されてめり込まないのが自然でしょうね。`destination`をめり込まない位置に補正してやる手もあるかもしれませんが、別な案として、パドルの移動方法を変えてみるというのはどうでしょう。
72
+ パドルを非キネマティックな通常の剛体に戻し、移動はパドルに`destination`座標へ向かう力をかけることにより行うことにしました(マウスポインタとパドルをダンパー付きの固いバネで繋ぐようなイメージで作ってみました...コード上で記述する代わりに、ジョイントをうまく使って同様の動作をさせることもできそうです)。
73
+ パドルが非キネマティックになったので、パックから受けた抗力によってパドルが移動できるようになりました。
74
+
75
+ ```C#
76
+ using UnityEngine;
77
+
78
+ [RequireComponent(typeof(Rigidbody))]
79
+ public class Mallet : MonoBehaviour
80
+ {
81
+ public Vector2 MovableRangeX = new Vector2(float.NegativeInfinity, float.PositiveInfinity);
82
+ public Vector2 MovableRangeZ = new Vector2(float.NegativeInfinity, float.PositiveInfinity);
83
+ [Range(0.0f, 10000.0f)] public float SpringStiffness = 1000.0f;
84
+ [Range(0.0f, 10000.0f)] public float VelocityDamper = 100.0f;
85
+ [Range(0.0f, 10000.0f)] public float DestinationDamper = 10.0f;
86
+
87
+ private Vector3 destination;
88
+ private new Rigidbody rigidbody;
89
+
90
+ private void Start()
91
+ {
92
+ this.rigidbody = this.GetComponent<Rigidbody>();
93
+ this.rigidbody.isKinematic = false; // パドルを非キネマティックに変更
94
+ this.destination = this.rigidbody.position;
95
+ }
96
+
97
+ private void Update()
98
+ {
99
+ this.SetDestination(Input.mousePosition);
100
+ }
101
+
102
+ private void SetDestination(Vector3 screenPoint)
103
+ {
104
+ const float dyThreshold = 0.001f;
105
+ var ray = Camera.main.ScreenPointToRay(screenPoint);
106
+ var oy = ray.origin.y;
107
+ var dy = ray.direction.y;
108
+ if (-dy > dyThreshold)
109
+ {
110
+ this.destination = ray.origin - ((oy / dy) * ray.direction);
111
+ this.destination.x = Mathf.Clamp(this.destination.x, this.MovableRangeX.x, this.MovableRangeX.y);
112
+ this.destination.z = Mathf.Clamp(this.destination.z, this.MovableRangeZ.x, this.MovableRangeZ.y);
113
+ }
114
+ }
115
+
116
+ private void FixedUpdate()
117
+ {
118
+ // パドルを目標地点(マウス位置)に引きつける力をかける
119
+ this.rigidbody.AddForce(this.GetSpringForce());
120
+ }
121
+
122
+ private Vector3 GetSpringForce()
123
+ {
124
+ var velocity = this.rigidbody.velocity;
125
+ var speed = velocity.magnitude;
126
+ var velocityDirection = speed < 1E-5f ? Vector3.zero : velocity / speed;
127
+ var relativePosition = this.destination - this.rigidbody.position;
128
+ var sqrDistance = relativePosition.sqrMagnitude;
129
+
130
+ // 目標地点とパドルの距離に比例する引っぱり力...とりあえず「バネ復元力」と呼ぶことにする
131
+ var springForce = relativePosition * this.SpringStiffness;
132
+
133
+ // パドルの速度に比例する抵抗力の大きさ...速度の過大な上昇を防ぐ
134
+ var velocityDamperForceMagnitude = this.VelocityDamper * speed;
135
+
136
+ // 目標地点とパドルの距離の2乗に逆比例する抵抗力の大きさ...目標地点に近づいたら急激なブレーキをかける
137
+ var destinationDamperForceMagnitude = this.DestinationDamper / sqrDistance;
138
+
139
+ // パドルの運動量の大きさ×秒間フレーム数
140
+ var momentumThreshold = (speed * this.rigidbody.mass) / Time.fixedDeltaTime;
141
+
142
+ // 抵抗力の大きさ...位置抵抗と速度抵抗の和
143
+ // ただしmomentumThresholdを越えないようにし、パドルの運動を完全停止させるのに
144
+ // 必要な以上の力が加わらないようにしたい(抵抗力によって運動方向が逆向きになってしまう状況を防ぐ)
145
+ // パドルは速度×質量の運動量を持っており、同量の力積を逆向きに加えれば完全停止する
146
+ // なので、1フレーム中に加わる力積がそれを越えないようにクランプしてやればいいはず
147
+ var damperForceMagnitude = Mathf.Min(
148
+ velocityDamperForceMagnitude + destinationDamperForceMagnitude,
149
+ momentumThreshold);
150
+
151
+ // 最終的な力は目標地点方向へのバネ復元力と速度方向と逆方向への抵抗力の和とする
152
+ var force = springForce - (damperForceMagnitude * velocityDirection);
153
+ return force;
154
+ }
155
+ }
156
+ ```
157
+
158
+ ![プレビュー](dc16a12f8f0aec50d80d7bf59cf99ad0.gif)