質量1kg、重力作用なしの球体に下記のようなスクリプトをアタッチし、挙動をちょっと確認してみました。
lang
1 using System.Collections;
2 using UnityEngine;
3
4 [RequireComponent(typeof(Rigidbody))]
5 public class AddForceAndVelocityTest : MonoBehaviour
6 {
7 [SerializeField] private Vector3 initialVelocity = Vector3.forward;
8 [SerializeField] private Vector3 impulseA = Vector3.right;
9 [SerializeField] private Vector3 impulseB = Vector3.up;
10 [SerializeField] private bool zeroesVelocity;
11
12 private Vector3 initialPosition;
13 private new Rigidbody rigidbody;
14 private bool triggered;
15
16 private void Start()
17 {
18 this.rigidbody = this.GetComponent<Rigidbody>();
19 this.rigidbody.velocity = this.initialVelocity;
20 this.initialPosition = this.rigidbody.position;
21 }
22
23 private void Update()
24 {
25 // スペースキーが押されたら、次回のFixedUpdateで実験を行うことにする
26 if (Input.GetKeyDown(KeyCode.Space))
27 {
28 this.triggered = true;
29 }
30
31 // リターンキーが押されたら初期状態にリセットし、実験をやり直せるようにする
32 if (Input.GetKeyDown(KeyCode.Return))
33 {
34 Debug.Log("Reset!");
35 this.rigidbody.position = this.initialPosition;
36 this.rigidbody.velocity = this.initialVelocity;
37 this.rigidbody.rotation = Quaternion.identity;
38 this.rigidbody.angularVelocity = Vector3.zero;
39 }
40 }
41
42 private void FixedUpdate()
43 {
44 if (!this.triggered)
45 {
46 return;
47 }
48
49 this.triggered = false;
50
51 // AddForce実行前の速度を出力してみる
52 Debug.Log($"Frame: {Time.frameCount}");
53 Debug.Log($"Velocity 0: {this.rigidbody.velocity}");
54
55 // impulseAを加え、その直後の速度を出力してみる
56 Debug.Log($"Add impulse A: {this.impulseA}");
57 this.rigidbody.AddForce(this.impulseA, ForceMode.Impulse);
58 Debug.Log($"Velocity 1: {this.rigidbody.velocity}");
59
60 // velocityをゼロにし、その直後の速度を出力してみる
61 if (this.zeroesVelocity)
62 {
63 this.rigidbody.velocity = Vector3.zero;
64 Debug.Log($"Velocity 1': {this.rigidbody.velocity}");
65 }
66
67 // impulseBを加え、その直後の速度を出力してみる
68 Debug.Log($"Add impulse B: {this.impulseB}");
69 this.rigidbody.AddForce(this.impulseB, ForceMode.Impulse);
70 Debug.Log($"Velocity 2: {this.rigidbody.velocity}");
71
72 this.StartCoroutine(this.PrintVelocityAfterPhysicsUpdate());
73 }
74
75 private IEnumerator PrintVelocityAfterPhysicsUpdate()
76 {
77 yield return new WaitForFixedUpdate();
78
79 // 最後に、WaitForFixedUpdateタイミングでもう一度速度を出力してみる
80 Debug.Log($"Frame: {Time.frameCount}");
81 Debug.Log($"Velocity 3: {this.rigidbody.velocity}");
82 }
83 }
velocity
をゼロにしない場合...
最初の速度は(0.0, 0.0, 1.0)
。
力積(1.0, 0.0, 0.0)
を加えても速度は(0.0, 0.0, 1.0)
のまま。
力積(0.0, 1.0, 0.0)
を加えても速度は(0.0, 0.0, 1.0)
のまま。
WaitForFixedUpdate
まで待つと加えた力積が反映され、速度は(0.0, 0.0, 1.0)
から(1.0, 1.0, 1.0)
に変わる。
velocity
をゼロにする場合...
最初の速度は(0.0, 0.0, 1.0)
。
力積(1.0, 0.0, 0.0)
を加えても速度は(0.0, 0.0, 1.0)
のまま。
速度を(0.0, 0.0, 0.0)
に書き換えると、直後に速度は(0.0, 0.0, 0.0)
に変わる。
力積(0.0, 1.0, 0.0)
を加えても速度は(0.0, 0.0, 0.0)
のまま。
WaitForFixedUpdate
まで待つと加えた力積が反映され、速度は(0.0, 0.0, 0.0)
から(1.0, 1.0, 0.0)
に変わる。
という風に、velocity
書き換えは即座に反映されるのに対し、AddForce
を行ってもすぐにはvelocity
は変化しない様子でした。
物理シミュレーションシステムの裏方ではPhysX が使われていたかと思うのですが、PhysXのドキュメント には...
The forces acting on a body are accumulated before each simulation frame, applied to the simulation, and then reset to zero in preparation for the next frame.
なんて記述がありました。おそらく1570pさんがおっしゃるようにAddForce
で加えられた力積は内部的にどんどん積算されていき、シミュレーションを進めるタイミング(「イベント関数の実行順序 」の図の中の「Internal physics update」の部分)でその時点のvelocity
に反映される仕組みなんじゃないかと思います。
ご質問者さんがおっしゃるような「コード上のある地点までのAddForce
を無効化させる」というのは難しいかもしれませんね。
どうやらPhisXにはclearForce なんて機能があるようですが、UnityのRigidbody
にはそれに相当するものはなさそうです。AddForce
によって加えられた力積を別途自前で集計しておいて、無効化させたい時にはそれの逆向きの力積を加える...みたいな方法で対処する必要があるんじゃないでしょうか。