Monthly Archives: 8月 2014

[Unity] オブジェクトを爆風で吹っ飛ばす

前回の続きです。
Rigidbodyがアタッチされたオブジェクトが風に煽られて動く動作を応用し、爆風で放射状にオブジェクトを吹っ飛ばす動作を実現したいと思います。

爆風はある点を基準に放射状に風が生じるものと仮定します。
すなわち、風を発生させるオブジェクトから吹っ飛ばされる対象のオブジェクトに向かうベクトルを取得し、このベクトルの向きに風速を発生させればよいです。
スクリプトは以下のようになります。

上記スクリプトを以下の要領でアタッチします。

 1.新規ゲームオブジェクト作成

 2.Rigidbodyをアタッチ・設定

 3.ShpereColliderをアタッチ・設定、Is Triggerにチェックを入れる

 4.スクリプトをアタッチ

 5.空気抵抗係数(Coefficient)、爆風の速さ(Speed)の設定

これで放射状の風が発生し、オブジェクトが四方八方に飛ばされるようになります。

explosion-demo

今回は厳密な風速計算は行っていないですが、それらしく見せることが目的なら十分ではないかと思います。

[Unity] Rigidbodyが風を受けるようにする

前回の記事で空気抵抗を独自に実装する方法を紹介しました。
今回はこの応用として風に煽られてオブジェクトが移動する動作を実現する方法を紹介したいと思います。

・風オブジェクトの実装
フィールド上に風を発生させるスクリプトを実装し、これをゲームオブジェクトにアタッチして風オブジェクトとします。
この時、局所的に風を発生させることを考慮してコライダーに接触するオブジェクトに対して風力を加えます。
そのため、風オブジェクトにはコライダーもアタッチしておきます。
風力を与える対象のオブジェクトはRigidbodyがアタッチされているものとします。

風を発生させるスクリプトは以下のようになります。

風オブジェクトの作成手順は下記の通りです。

 1.新規ゲームオブジェクト作成

 2.Rigidbodyをアタッチ・設定

 3.コライダーをアタッチ・設定、Is Triggerにチェックを入れる

 4.スクリプトをアタッチ

 5.空気抵抗係数(Coefficient)、風速(Velocity)の設定

Coefficient=3、Velocity=(0,5,5)と設定すれば、以下のようにオブジェクトが風力を受けて前方上に飛ばされるようになります。

wind-demo

風オブジェクトのコライダーはSphereColliderとしてその領域を半透明で表示しています。

今回は風速が固定です。
すなわち常に同じ向き・大きさです。

この風速を放射状に設定すればヘリコプター効果や爆風などを再現できます。
他にも活用できる場面は色々あるのではないかと思います。

[Unity] 非アクティブなゲームオブジェクトを取得する

スクリプト上から自分の子階層にあるゲームオブジェクトをFindで取得する場合、以下のようにスクリプトを記述します。

しかし、上記の検索で取得できるのはアクティブになっているゲームオブジェクトのみで、非アクティブなゲームオブジェクトは取得できません。

実は子階層にあるゲームオブジェクトに限り、非アクティブでも取得する方法があります。
以下のように自身のtransformフィールドからFindメソッドを実行して取得します。

しかし、この方法はパフォーマンスが若干落ちるので、よほどでない限り使用しないほうが良いでしょう。

[Unity] 空気抵抗を独自に実装してみる

Unityでは空気抵抗を受けるシミュレーションが可能です。
対象のゲームオブジェクトにRigidbodyをアタッチし、Dragの値を0以外に設定すれば実現できます。

air-resist1

しかし、この空気抵抗の動作には大きな問題があります。

通常、物体に及ぼす空気抵抗は力であるため、質量が異なる同じ形状・大きさの物体を落下するとき、質量の軽い物体のほうが終端速度が低くゆっくりと落下します。(ニュートンの運動方程式より)

Unityでは上記物体は同じ速度で落下します。
すなわち、物体の質量にかかわらず空気抵抗が及ぼされる挙動は変わりません。
花びらや鉄くずも同じ速度で落下します。

上記のままでは扱いづらいと思う人もいるでしょう。(私がそうです)
もっと厳密に空気抵抗による動きを再現したい場合は独自に物理計算処理を実装するしかありません。
今回は以下のような簡単な運動方程式で実現してみます。

F = -kv
 
Fは物体に及ぼす空気抵抗の抗力、vは物体の移動速度、kは空気抵抗係数(>0)です。
スクリプトで実装すると以下のような感じになります。

このスクリプトを空気抵抗を受けるゲームオブジェクトにアタッチします。
アタッチしたらcoefficientに適当な値を設定します。
(RigidbodyのMassが1の場合は5くらいを設定すれば効果を確認できます)
RigidbodyのDragは0にします。

air-resist2

これでRigidbodyの質量によって落下速度が変わるようになりました。

fall-ball

左端の球体が最も軽く、右に行くほどMassを1ずつ大きくしています。

今回は簡単な運動方程式で実装しましたが、これは厳密に成り立つものではなく弾丸や飛行機など高速で移動する物体に対しては以下のような式を実装する必要があります。

F = \cfrac{\rho C S v^2}{2}
 
ρは空気密度、Cは空気抵抗係数、Sは断面積、vは物体に対する空気の相対速度です。
設定するパラメータが増えて面倒になる反面、より正確な動きを再現できます。
数式が変わるだけでFixedUpdate内でRigidbodyに対してAddForceで上式を突っ込んでやるだけです。

断面積を計算するのが面倒ですが、これができると板のような平べったい物体がひらひら舞い落ちる挙動などが実現できます。

これはまたの機会に書きたいと思います。

[Unity] 惑星に向かって物体が落ちるようにする

前回の記事で個別のゲームオブジェクトに対して重力加速度を与える方法について紹介しました。

今回はこの応用として引力の発生する小さな惑星に物体が落ちるような動きを実現してみたいと思います。

・単純な方法
大きさが一定の加速度ベクトルを球体の中心に向かせれば惑星に物体が落下するようになります。

これで以下のように惑星に向かってものが落ちていくようになります。

planet-gravity-1

・万有引力の法則を使う
上記の方法では惑星からどれだけ離れても重力加速度の大きさは一定です。
しかし、実際は惑星から離れるほど小さくなって行きます。
これは以下のような万有引力の法則に基づいて決まります。

F = G \cfrac{m_1 m_2}{r^2}
 
Gは定数、m_1, m_2は落下する物体の質量、rは惑星からの距離です。
より正確な動きを再現する場合は上記の数式に使われるパラメータを定めて式を実装する必要があります。

実際のスクリプトにすると以下のような感じになります。

これで以下のように複数個の惑星があっても各星に向かって物体が落ちていくようになります。

planet-gravity-2

上記のスクリプトは惑星に対して引力を作用させることはやっていないので、厳密に実装する場合はフィールド上に存在する物体をすべて検索で取得し、相互作用を及ぼすようにする必要があるでしょう。
しかし、惑星が動かないことが前提なら上記のように多少手抜きをして計算量を減らしたほうがよいと思います。(フィールドに存在する物体の2乗に比例して計算量が増大するため)

万有引力の法則に従って重力加速度を発生させているので、物体が惑星の周りを回る動作も再現できます。
宇宙船がスイングバイするゲームや惑星同士が戦うゲームなども作れそうですね。

■参考サイト
万有引力の法則・定理のまとめ / 物理 by 藤山不二雄 |マナペディア|

[Unity] 重力についてあれこれ

Unityでは、Rigidbodyをアタッチしたオブジェクトに対して剛体の物理演算が働きます。
物体が落下していく動きも物理演算です。

物体の落下動作は、物体に重力加速度g(=9.81)をy軸の負の方向に与えることによって実現しています。
重力加速度の設定はメニューの「Edit」→「Project Settings」→「Physics」でPhysicsManagerを開き、Gravityから設定できます。
デフォルトではX=0, Y=-9.81, Z=0になっていると思います。

x軸方向に0以外の値を指定すれば、横方向に物体が落下(?)していきます。
全部0にすれば無重力状態になります。
この重力加速度はスクリプトからも変更できます。

上記のスクリプトを実行すると、X=0, Y=9.81, Z=0と値が上書きされて上向きに物体が落ちていきます。

このように、物体が落下するのはPhysics.gravityという加速度が常に物体にかかっているためです。
Physics.gravityの加速度を与えないようにするには、インスペクタの「Rigidbody」→「Use Gravity」のチェックをはずします。
これで自由に重力を設定したり重力発生の有無を指定できるようになりました。
しかし、物体それぞれに違う重力加速度を与えたい場合や、万有引力のように星の中心に重力加速度を発生させたい場合には対応できません。

このような場合、以下のようなRigidbodyを持つゲームオブジェクトに対して指定された加速度を継続的に発生させるスクリプトをアタッチすることで実現します。

上記スクリプトのaccelerationの値をPhysics.gravityと同じにすればUse Gravityを使ったときと全く同じ挙動になります。

壁に向かうように加速度を設定すれば壁を伝って歩く動作を実現できます。
小さな星の上を歩くような動作だと、星の中心に向かうように加速度を設定すれば実現できます。

[Unity] 自前のクラスをインスペクタから編集できるようにする

Unityではスクリプトで定義したクラスのフィールドをPublicにするか[SerializeField]属性を頭に付けることでインスペクタから編集できるようになります。
しかし、自前で定義した構造体やクラスは上記のことをやってもインスペクタ上には表示されません。

たとえば以下のようなスクリプトをゲームオブジェクトにアタッチした場合、インスペクタ上では何も表示されません。

unity-class-ins1

独自で定義した構造体のフィールドはインスペクタから参照できないので諦めるしかないですが、クラスは参照させることができます。
クラスの前に[System.Serializable]属性をつけます。

unity-class-ins2

これでめでたしめでたし・・・かと思いきや、以下のようにテンプレート引数を持つジェネリッククラスでは[System.Serializable]属性をつけてもインスペクタに表示されません。

どうしてもインスペクタに表示させたい場合は、ジェネリッククラスからジェネリックではないサブクラスを作って使うしか無いようです。

ジェネリッククラスもインスペクタから参照できるようになってほしいですが、これは今後のバージョンアップに期待することとしましょう。

■参考サイト
Unity – Scripting API: Serializable
UnityのInspectorで変数を表示する方法まとめ – Qiita
C#で構造体をInspectorで使える様にする方法 – 強火で進め
[Unity]ジェネリッククラスをInspectorに表示するにはどうすればいいの? | ケットシーウェア

[Unity] Rigidbodyのスリープ問題

2ヶ月くらい前のゲームジャムで嵌っていた問題のメモ書きです。

玉転がしゲームで以下のように水平面に球体を落として平面を回転させたとき、ボールがその場で止まってしまいます。

rbbody-sleep

分かり易くするため、ボールがスリープ状態のときは赤色に、そうでないときは緑色に表示しています。
ボールが平面に完全に静止したあとにスリープし、その後に平面を傾けてもスリープ状態のまま動かなくなっていることが確認できます。

平面をほんのわずかでも傾けておくと上記の問題は発生しなくなります。

Unityのリファレンスマニュアルによると、Rigidbody自体がスリープ状態に入ってしまうと以下の条件を満たさない限りウェイクアップしないためです。

  ・別の Rigidbody がスリープしているRigidbodyに衝突した場合
  ・ジョイントを通じて接続された別のRigidbodyが移動し始めた場合
  ・Rigidbody のプロパティ編集時
  ・Forceの追加時

上記の例では、平面を傾けてもボールに衝突したりしないため結果として空中停止していました。
このような現象を解決するためには、平面を動かすとき等、ボールが動いてほしいタイミングでスクリプトからウェイクアップする必要があります。

以下のようなスクリプトを追加してキー入力で明示的にボールのRigidbodyをウェイクアップさせてみました。

rbbody-sleep2

ゲーム画面にフォーカスが当たった状態で「A」キーを押すと強制的にウェイクアップが掛かり、静止していたボールが動き出すようになりました。

物体がウェイクアップして動き出す動作は通常は意識することが無いですが、もし予期せずにその場でとまってしまうことがあったら上記を疑ってみると良いかもしれませんね。

■参考サイト
Rigidbody のスリープ / Rigidbody Sleeping

[Unity] AddForceの実行タイミングについて

Unityを扱っているときに嵌ったことのメモです。

Rigidbodyを使ったGameObjectに対してRigidbody.AddForce()メソッドで力や加速度を加えたい場合、以下のようにして毎フレーム呼び出されるコールバックの中で処理を行うとします。

しかし、このUpdate()メソッド内で呼び出した場合、FPSが固定でない場合に物体の動きが変わってしまう場合があります。
FPSが高いときは強い力が加わり、FPSが小さいときは力が弱くなってしまいます。
これは、AddForceメソッドは固定のフレームレートで呼び出されることを想定して設計されているためです。

したがって、固定のフレームレートで呼び出されるFixedUpdate()メソッド内で呼び出すことで上記の問題は解決できます。

リファレンスにもこのことについて記載されていますが、見落としがちな注意点として挙げておきました。

■参考サイト
http://docs.unity3d.com/ScriptReference/MonoBehaviour.FixedUpdate.html

[風使い(仮)] 空飛ぶボール

8月より制作を開始したゲーム「風使い(仮)」の進捗状況です。
ボールが風に煽られて空を飛ぶようになりました。

kaze-fly

ボールが風に煽られて飛ぶ動作は簡単な物理演算で実現しています。
操作はマウスで行うようにしています。
まだ静止画のみしかお見せ出来ない状態です…

左クリックでカメラ前方に、右クリック+ドラッグでカメラ横方向にドラッグした分だけ風力を発生させるようにしました。

上記操作を組み合わせることで、ラジコン飛行機のようにボールを飛ばせるようになります。
魔法の絨毯に乗っているような感覚とも言えそうです。
ある程度動きが完成したら動画でもアップしたいと思います。

お次はステージ制作です。
最初のステージは自然の中を風に乗って飛行するリラックスできる雰囲気にしたいと考えています。