前回、FixedJointを学習したが今回はバネの動きを実現するSpringJointを学習しよう。
プロジェクトの作成
いつものように一緒に手を動かしていこう。まずは新規3Dプロジェクトを作成してSpringJointExというプロジェクトを作成しよう。

○新規にCubeを作成し、PositionのYに5を入力してRigidbodyを付与する。
その際、Use Gravityはオフにし、isKinematicをオンにする。isKinematicをオンにすることでこのオブジェクトは外部の物理的影響を受けなくなる。

○同じようにもう一つCubeを作成する。Rigidbodyを付与し、今回の主役であるSpringJointコンポーネントを付与する。ConnectedBodyに最初に作成したCubeを登録してみよう。

確認
これで2つのRigidbodyがスプリングで連結された。実行してシーンビューで1つ目のCubeを左右に動かしてみよう。
○SpringJointによって2つのCubeが連動して動くことがわかる。
アンカーの調整
スプリングの動きが確認できたが、細かいところを深堀りしてみよう。スプリングなので当然2つの点を結ばなければならない。その点を決めるのがAnchorだYに0.5と入っているのはこのJointのついたオブジェクトのローカル座標を表している。0.0ならばこのキューブの中心が設定される。今回は0.5なのでこのキューブの上面に接合点が設定されたよく見るとオレンジでAnchorが表示されている。

もう片方のAnchorは?
Auto Configure Connectedにチェックが入っていると自動的に接続される側のAnchorが設定される。-1.5というのは上のキューブから見たローカル座標で、1つ目の点と同じ位置が設定されている。

確認
実行してみよう。実際にはCubeとCubeの間は何も描画されないが、図で示すと以下のような感じだ。赤線の先に上のCubeのアンカーがあり、そこからスプリングで下のCubeにつながっている。いま、スプリングが伸びているのは下のCubeに対して下向きの重量が働いているからだ。

Anchorの変更
空間にAnchorがあるのも変なので上のCubeの底面に移動しよう。
AutoConfigure Connectedのチェックを外して、AnchorのYを-0.5に設定する。

上のCubeの底面と、下のCubeの上面がスプリングで結ばれた。実行して先ほどと同じように上のキューブを左右に動かしてみよう。よりナチュラルに振動することがわかる。

バネの隙間を考える
今、CubeとCubeの間に隙間が空いているのは下のCubeに対して下向きの重力が働いてスプリング伸びているからだ。この隙間を0にするのにはどうすればよいのか考えてみよう。
○答え1
下のCubeのUse Gravityをオフにする。
重力が働かなければバネの力によって2つのCubeの隙間はなくなる。(現実のバネとは違い長さ0まで収縮可能)

○答え2
バネの力を強くする。重力などものともしないくらい強い力をバネに発生させればよさそうだ。UseGravityのチェックはもとに戻してSpringを1000に設定してみよう。

Spring影響範囲の調整
さきほど、springの力を1000にすることで2つのCubeがくっつくことを確認したが、spring力の影響を受けない範囲を設定することで隙間を調整することができる。以下のようにMin Distance とMax Distanceを調整してみよう。

2つのアンカーがこのMinとMaxの距離に入っている間はスプリングの力を一切受けない。詳しくは以下の動画を見ること。
Anchorから3mまでは糸で結ばれている状態をイメージしてもよいだろう。(3mを超えるとバネになる)
Minにも値を入れた場合
先程はMinに0を入れたがここに3などを入れた場合、スプリング無効ゾーンはドーナツ状になる。この状態でドーナツの中心方向にAnchorが移動する場合外側にスプリング力が働くこととなる。(赤の矢印の向きにスプリング力が働く)

応用
では少しゲームづくりに役立ちそうな動きを入れて知見を深めよう。
地面の追加
○新規Planeを作成する

○一旦SpringJointをリムーブする。実行してみよう、そのまま重力に従って落下するようになった。

○足場の作成
新規にCubeを作成し、以下のようにトランスフォームを設定する。

足場ができて落下しなくなった。

○上にあるCubeをPlayerにリネームしてトランスフォームを調整する

Playerスクリプトの作成
以下のようにPlayerスクリプトを作成し、Playerにアタッチする。
using UnityEngine;
public class Player : MonoBehaviour {
public GameObject block;
bool isConnect;
void Update() {
//右に移動
transform.Translate(Vector3.right * Time.deltaTime);
//もしコネクトしていなくて、x座標が0まできたら
if (!isConnect && transform.position.x >= 0f) {
//blockにSpringJointを追加
SpringJoint sj = block.AddComponent<SpringJoint>();
//connectedBodyにPlayerのRigidbodyを設定
sj.connectedBody = GetComponent<Rigidbody>();
//connectedanchorの自動設定をオフ
sj.autoConfigureConnectedAnchor = false;
//connectedAnchorの設定
sj.connectedAnchor = new Vector3(0f, -0.5f, 0f);
//スプリングの強さを設定
sj.spring = 10.0f;
//フラグ反転(処理を一回こっきりにしている)
isConnect = true;
}
}
}
○くっつけたいCubeを登録する

○実行してみよう。原点まで来たらブロックが連結されることがわかる。

障害物の追加
○新規にCubeを作成して以下のようにトランスフォームを調整しよう。

○実行してみよう。スプリングの効果がよくわかる。
衝突について
実行してみるとわかるがスプリングJointで結ばれた2つのオブジェクト間での衝突は発生しない。

この挙動が気に入らないときはSpringJointのEnable Collisionを有効にする。
以下のようにスクリプトを変更する。
using UnityEngine;
public class Player : MonoBehaviour {
public GameObject block;
bool isConnect;
void Update() {
//右に移動
transform.Translate(Vector3.right * Time.deltaTime);
//もしコネクトしていなくて、x座標が0まできたら
if (!isConnect && transform.position.x >= 0f) {
//blockにSpringJointを追加
SpringJoint sj = block.AddComponent<SpringJoint>();
//connectedBodyにPlayerのRigidbodyを設定
sj.connectedBody = GetComponent<Rigidbody>();
//connectedanchorの自動設定をオフ
sj.autoConfigureConnectedAnchor = false;
//connectedAnchorの設定
sj.connectedAnchor = new Vector3(0f, -0.5f, 0f);
//スプリングの強さを設定
sj.spring = 10.0f;
//接続されているオブジェクト同士の衝突を有効にする
sj.enableCollision = true;
//フラグ反転(処理を一回こっきりにしている)
isConnect = true;
}
}
}
実行してみよう。ジョイントされた2つのオブジェクトで衝突が発生するようになった。
参考
今回は動的にSpringJointをアタッチしたが、インスペクターから設定するときは以下のEnable Collisionをオンにする。

Break Force
スプリングに力がかかったときにJointが破棄される設定ができる。
スクリプトを以下のように修正しよう。
using UnityEngine;
public class Player : MonoBehaviour {
public GameObject block;
bool isConnect;
void Update() {
//右に移動
transform.Translate(Vector3.right * Time.deltaTime);
//もしコネクトしていなくて、x座標が0まできたら
if (!isConnect && transform.position.x >= 0f) {
//blockにSpringJointを追加
SpringJoint sj = block.AddComponent<SpringJoint>();
//connectedBodyにPlayerのRigidbodyを設定
sj.connectedBody = GetComponent<Rigidbody>();
//connectedanchorの自動設定をオフ
sj.autoConfigureConnectedAnchor = false;
//connectedAnchorの設定
sj.connectedAnchor = new Vector3(0f, -0.5f, 0f);
//スプリングの強さを設定
sj.spring = 10.0f;
//接続されているオブジェクト同士の衝突を有効にする
sj.enableCollision = true;
//breakForce設定
sj.breakForce = 12.0f;
//フラグ反転(処理を一回こっきりにしている)
isConnect = true;
}
}
}
○実行してみよう。力が加わったときにJointが破棄されることがわかる。
終わりに
今回はスプリングの動きを実現するスプリングジョイントを学習した。
ゲーム内の物理表現をリアルで魅力的にすることができるので、シーンに動きを追加したい場合にぜひ活用してほしい。
コメント