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

回答編集履歴

3

angularVelocity版での移動量が過剰だったため修正

2018/07/14 22:21

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -101,9 +101,10 @@
101
101
  private void FixedUpdate()
102
102
  {
103
103
  var velocity = this.rigidbody.velocity;
104
- this.rigidbody.MovePosition(Vector3.SmoothDamp(this.rigidbody.position, this.destination, ref velocity, 1.0f, 10.0f, Time.fixedDeltaTime));
104
+ Vector3.SmoothDamp(this.rigidbody.position, this.destination, ref velocity, 1.0f, 10.0f, Time.fixedDeltaTime);
105
105
  // 非キネマティックに変えたので、MovePositionによる位置操作の結果が自動的にrigidbody.velocityに
106
106
  // 反映されなくなるため、更新されたvelocityをrigidbody.velocityに再代入してやる
107
+ // SmoothDampの返り値は破棄し、rigidbody.velocity書き換えによってのみ移動させることでつじつまを合わせる
107
108
  this.rigidbody.velocity = velocity;
108
109
 
109
110
  // 先に投稿したコードのtranslationをvelocityに置き換えると、得られる回転角は

2

angularVelocityについて追記

2018/07/14 22:21

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -61,4 +61,63 @@
61
61
 
62
62
  このような動きになりました。見やすくするため地面オブジェクトを配置しましたが、動きはスクリプトで制御されているので、球が地面から外れても落下しません。
63
63
 
64
- ![結果](8d1893de7a5b4be17271bf9f4da722a5.gif)
64
+ ![結果](8d1893de7a5b4be17271bf9f4da722a5.gif)
65
+
66
+ ### 追記
67
+ [angularVelocity](https://docs.unity3d.com/ScriptReference/Rigidbody-angularVelocity.html)の書き換えで回転させたい場合、オブジェクトを非キネマティックにしないとならないようです。
68
+ ですので、適宜重力を作用させなくしたり、他のオブジェクトから作用を受けないようにレイヤーを変更したりといった設定調整が必要かもしれません。
69
+ angularVelocity方式の例としては、こんな感じでどうでしょうか?
70
+
71
+ ```C#
72
+ using UnityEngine;
73
+
74
+ [RequireComponent(typeof(Rigidbody), typeof(SphereCollider))]
75
+ public class NonKinematicSphereController : MonoBehaviour
76
+ {
77
+ private Vector3 destination;
78
+ private new Rigidbody rigidbody;
79
+ private SphereCollider sphereCollider;
80
+
81
+ private void Start()
82
+ {
83
+ this.rigidbody = this.GetComponent<Rigidbody>();
84
+ this.sphereCollider = this.GetComponent<SphereCollider>();
85
+ this.rigidbody.isKinematic = false; // 非キネマティックにする場合...
86
+ this.rigidbody.useGravity = false; // 重力を作用させなくする
87
+ }
88
+
89
+ private void Update()
90
+ {
91
+ var plane = new Plane(Vector3.up, this.rigidbody.position);
92
+ var ray = Camera.main.ScreenPointToRay(Input.mousePosition);
93
+ float rayDistance;
94
+ plane.Raycast(ray, out rayDistance);
95
+ if (rayDistance > 0.0f)
96
+ {
97
+ this.destination = ray.GetPoint(rayDistance);
98
+ }
99
+ }
100
+
101
+ private void FixedUpdate()
102
+ {
103
+ var velocity = this.rigidbody.velocity;
104
+ this.rigidbody.MovePosition(Vector3.SmoothDamp(this.rigidbody.position, this.destination, ref velocity, 1.0f, 10.0f, Time.fixedDeltaTime));
105
+ // 非キネマティックに変えたので、MovePositionによる位置操作の結果が自動的にrigidbody.velocityに
106
+ // 反映されなくなるため、更新されたvelocityをrigidbody.velocityに再代入してやる
107
+ this.rigidbody.velocity = velocity;
108
+
109
+ // 先に投稿したコードのtranslationをvelocityに置き換えると、得られる回転角は
110
+ // 1秒あたりの弧度法による回転量...つまり角速度の大きさとなる
111
+ // 回転軸も前回と同様の方法で求め、これが角速度ベクトルの向きとなる
112
+ // あとは求めた角速度ベクトル(向きaxis、大きさangularSpeed)を
113
+ // rigidbody.angularVelocityに代入すればいいはず
114
+
115
+ var speed = velocity.magnitude; // 速さ
116
+ var scaleXYZ = transform.lossyScale; // ワールド空間でのスケール推定値
117
+ var scale = Mathf.Max(scaleXYZ.x, scaleXYZ.y, scaleXYZ.z); // 各軸のうち最大のスケール
118
+ var angularSpeed = speed / (this.sphereCollider.radius * scale); // 角速度の大きさ
119
+ var axis = Vector3.Cross(Vector3.up, velocity).normalized; // 角速度の向き
120
+ this.rigidbody.angularVelocity = angularSpeed * axis;
121
+ }
122
+ }
123
+ ```

1

球の半径がスケールを反映していなかったため修正

2018/07/14 21:59

投稿

Bongo
Bongo

スコア10816

answer CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  あるフレームで球が`rigidbody.velocoty * Time.deltaTime`移動したとし、これを`translation`とします。
4
4
  移動距離は`translation.magnitude`で、これを`distance`とします。
5
- の半径`sphereCollider.radius`ですので、弧度法による回転量は`distance / sphereCollider.radius`となります。
5
+ オブジェクトスケールを`scale`とすると、コライダーの本来の半径`sphereCollider.radius`ですので、球の半径は`this.sphereCollider.radius * scale`となり、弧度法による回転量は`distance / (this.sphereCollider.radius * scale)`となります。
6
6
 
7
7
  ![移動量と回転角](1f23a990a438813c3d7f81df8363d8fb.gif)
8
8
 
@@ -38,10 +38,12 @@
38
38
  {
39
39
  this.destination = ray.GetPoint(rayDistance);
40
40
  }
41
-
41
+
42
42
  var translation = this.rigidbody.velocity * Time.deltaTime; // 位置の変化量
43
43
  var distance = translation.magnitude; // 移動した距離
44
+ var scaleXYZ = transform.lossyScale; // ワールド空間でのスケール推定値
45
+ var scale = Mathf.Max(scaleXYZ.x, scaleXYZ.y, scaleXYZ.z); // 各軸のうち最大のスケール
44
- var angle = distance / this.sphereCollider.radius; // 球が回転するべき量
46
+ var angle = distance / (this.sphereCollider.radius * scale); // 球が回転するべき量
45
47
  var axis = Vector3.Cross(Vector3.up, translation).normalized; // 球が回転するべき軸
46
48
  var deltaRotation = Quaternion.AngleAxis(angle * Mathf.Rad2Deg, axis); // 現在の回転に加えるべき回転
47
49