最近暑くなってきましたね。
今回はそんな夏の暑さを吹き飛ばすべく、Rigidbody.AddForceAtPosition()
を使ってオブジェクトを吹き飛ばしていこうと思います。
きっと暑さは吹き飛ばないでしょうけど……。
この記事にはUnity2017.1.0f3を使用しています。
AddForceAtPositionって何だろう?
Rigidbody.AddForceAtPosition()
とは何なのでしょうか。
僕自身、あまりRigidbody
を使ってきていないので、少し調べて見ましょう。
Rigidbody
の有名どころにAddForce()
がありますよね。
docs.unity3d.com
これを使うと、指定した方向に飛んでいきます。
using UnityEngine; [RequireComponent( typeof( Rigidbody ) )] public class TestObject : MonoBehaviour { [SerializeField] private float m_power = 0.0f; [SerializeField] private Vector3 m_powerDir = Vector3.zero; private void FixedUpdate() { if( Input.GetMouseButtonDown( 0 ) ) { Rigidbody rigidbody = GetComponent<Rigidbody>(); rigidbody.AddForce( m_powerDir.normalized * m_power ); } } } // class TestObject
AddTorque()
というものもあるみたいです。
こちらを使うと、指定した方向に回転し始めます。
using UnityEngine; [RequireComponent( typeof( Rigidbody ) )] public class TestObject : MonoBehaviour { [SerializeField] private float m_power = 0.0f; [SerializeField] private Vector3 m_powerDir = Vector3.zero; private void FixedUpdate() { if( Input.GetMouseButtonDown( 0 ) ) { Rigidbody rigidbody = GetComponent<Rigidbody>(); rigidbody.AddTorque( m_powerDir.normalized * m_power ); } } } // class TestObject
ではAddForceAtPosition()
はどんなものなのでしょうか。
力と位置を渡してあげる必要があるみたいですね。
ポイントして、第二引数として渡す位置はワールド座標であるところですね。ローカル座標じゃないんですね。
ではオブジェクトの中心より少し上の辺りに力を加える処理を書いてみましょう。
using UnityEngine; [RequireComponent( typeof( Rigidbody ) )] public class TestObject : MonoBehaviour { [SerializeField] private float m_power = 0.0f; [SerializeField] private Vector3 m_powerDir = Vector3.zero; [SerializeField] private Vector3 m_offset = Vector3.zero; private void FixedUpdate() { if( Input.GetMouseButtonDown( 0 ) ) { Rigidbody rigidbody = GetComponent<Rigidbody>(); rigidbody.AddForceAtPosition( m_powerDir.normalized * m_power, transform.position + m_offset ); } } } // class TestObject
くるくると回りながら、飛んでいきました。
AddForce()
とAddTorque()
を足したような挙動です。
なるほど、これは楽しい関数です。
AddForceAtPositionで指定する位置について
さて、先ほど第二引数として渡す位置はワールド座標であるとお話しました。
ということはRigidbody
がアタッチしてあるオブジェクトの位置とかけ離れた位置にAddForceAtPosition()
の力を指定してしまうと、力は発揮されないのでしょうか。
上の図では、オブジェクトと明後日の位置に力が掛けられています。
これでは力が外れて効果がないように見えますが……。
調べたところそういうわけでは無いようですね。
AddForceAtPosition()
は別にアタッチしているメッシュやコリジョンを加味して力の計算を行っているわけではないみたいです。
そうでなければ、Rigidbody
しかアタッチしていないGameObject
ではAddForceAtPosition
が使えないことになってしまいますし。
さきほどAddForceAtPosition
を使ったときも、カプセルの中心から1m上に力を加えていますので、カプセル外の位置に力を加えています。
それでもちゃんと動いてますしね。
図にするとこんな感じでしょうか。
……うまく説明できていない気がする。
挙動で見てみましょう。
横一列にオブジェクトを配置して、全てのオブジェクトに同じ力と位置をAddForceAtPosition()
を使って与えるスクリプトを記述しましょう。
using UnityEngine; public class TestController : MonoBehaviour { [SerializeField] private float m_power = 0.0f; [SerializeField] private Vector3 m_powerDir = Vector3.zero; [SerializeField] private Rigidbody[] m_rigidbodies = null; private void FixedUpdate() { if( Input.GetMouseButtonDown( 0 ) ) { foreach( var rigidbody in m_rigidbodies ) { rigidbody.AddForceAtPosition( m_powerDir.normalized * m_power, Vector3.zero ); } } } } // class TestController
上記のスクリプトでは全て原点(0,0,0)の位置から真上(0,1,0)の力を発生させます。
さて、さっそく動作させてみましょう。
オブジェクトの移動する速さは全てのオブジェクトで同じですが、回転は異なっていますね。
そのため、AddForceAtPosition()
に指定する位置は、指定した位置で力が発生するわけではない点にご注意ください。
僕はAddForceAtPosition()
を使う前はそういう挙動なのだと思っていました。
まとめ
上記までのスクリプトでは第三引数を省略していますので、渡す力のタイプはForceMode.Force
を使用しています。
ただ叩く、蹴る、などの瞬間的な力を与える場合には、ForceMode.Impulse
を使った方がいいかもしれませんね。
マウスをクリックするたびに、適当な方向に瞬間的な力を加えるスクリプトを記述してみました。
使うとこんな挙動になります。
using UnityEngine; [RequireComponent( typeof( Rigidbody ) )] public class TestObject : MonoBehaviour { [SerializeField] private float m_power = 0.0f; [SerializeField] private float m_offsetRange = 2.0f; private void FixedUpdate() { if( !Input.GetMouseButtonDown( 0 ) ) { return; } Rigidbody rigidbody = GetComponent<Rigidbody>(); Vector3 randomDir = ( Random.insideUnitSphere - Vector3.zero ).normalized; float randomLength = Random.Range( -m_offsetRange, m_offsetRange ); Vector3 power = m_power * randomDir; Vector3 pos = transform.position + transform.up * randomLength; rigidbody.AddForceAtPosition( power, pos, ForceMode.Impulse ); } } // class TestObject
脳内の様子。 pic.twitter.com/ESFGP5Pac6
— うら干物 (@urahimono_maigo) 2017年8月17日
……まるでカプセルをいじめているようだ。
というわけで夏の暑さに負けずスクリプトを組んでみてくださいねー。