テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】別AssetBundleへ分割したモデルが、分離されず元シーンに残ってるケースについて

f:id:tsubaki_t1:20160817220750j:plain

AssetBundleにて依存先と依存元を分割すれば、ロード時間やアセットのメモリ使用量等を上手く節約出来ます。

ただ、このアセットの分割でえすが分割されないケースがあります。

AssetBundleを分割する

tsubakit1.hateblo.jp

AssetBundleが分割されないケース

AssetBundleが分割されないケースは、以下の様な手順で確認出来ます。

  1. まずボールのモデルをprefab、シーンをsceneのAssetBundleに設定します。
  2. 次に、ステージ上の全てのアセットにstaticを設定します。
  3. 後はstageシーンをAssetBundleからロードすると、prefab assetbundleを読み込まなくともモデルが含まれています

f:id:tsubaki_t1:20160817221056j:plain

f:id:tsubaki_t1:20160817221618j:plain

 

f:id:tsubaki_t1:20160817221729j:plain

f:id:tsubaki_t1:20160817221737j:plain

BatchingStaticしたメッシュは結合する

この謎を解く鍵は、ロードしたアセットのメッシュをよく見ると分かります。通常であれば、メッシュを読み込む場合、読み込み先のメッシュが指定されます。今回の場合RollerBallが指定されているはずです。
しかし実行時にはCombine Meshと差し替わっています。

f:id:tsubaki_t1:20160817222321j:plain

今回の重要な事は、Batching Staticを設定している事です。staticにチェックを入れると、Lightmap staticやNavmeshStaticの他、全てのstaticにチェックが入ります

f:id:tsubaki_t1:20160817222857j:plain

ここでbatching staticに設定されているアセットは、ビルド時に結合(実際にメッシュを結合する訳では無い模様)され、参照元のメッシュとは異なるメッシュとして登録されるみたいです。そのため、今回紹介したような事象が起こった…という訳です。

実際にcombine meshを確認すると、見事に結合されてます。

f:id:tsubaki_t1:20160817223651j:plain

実際にbatching staticを外すと、ちゃんと期待通りに動作します。ただしbatcingは効きにくくなるので、そこはケースバイケースで。

f:id:tsubaki_t1:20160817224526j:plain

ちなみに、この現象はたぶん「シーンをAssetBundle化した」場合起こります。static設定はあくまで「シーン内で静的であること」が前提みたいなので、prefabにstaticを設定してもコレは起こらないと思います。

 

結構ゲームを見てると「とりあえずstatic」付けてる人は多いですが、実際にはOccluder staticやLightmap Static等、ちゃんと判断して設定する必要がある項目もあるので、注意が必要かなと思わなくも。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】Rigidbody2Dがスリープになったかどうか確認する

f:id:tsubaki_t1:20160809224420g:plain

物理演算はスリープしてCPUを節約する

物理演算は複雑な当たり判定と物理的な動作のシミュレーション(再現)を行います。ただゲームのCPUにも限度という物があるため、物理演算はたいてい接触しない・動かないオブジェクトはスリープ状態へ移行する機能を持っています。

http://tsubakit1.sakura.ne.jp/images/201401200211573c3.gif

スリープしているオブジェクトは当たり判定の処理や物理演算による計算をスキップするため、動いている時と比べてかなりCPU負荷が下がります。

f:id:tsubaki_t1:20160809232843j:plain

物理演算の状態をシーンビューに表示する

気づいたら、Rigidbody2Dの設定に、物理演算がスリープに入ったかどうかを確認するGizmoを表示する機能が追加されていました。
設定すると、オブジェクトが現在スリープ状態なのか、アクティブなのかを確認することが出来ます。

f:id:tsubaki_t1:20160809225900j:plain

また同時に、物理演算の接点を矢印で表示してくれます。力の強さに応じて矢印を伸ばしてくれる訳ではないので少し分かりにくいですが。

f:id:tsubaki_t1:20160809230155j:plain

スリープを知るための設定方法

  1. まずEdit > PlayerSettings > Physics2DでPhysics2D Settingsを開きます。
  2. Gizmosを開きます。
  3. Always Show Colliders(常にコライダーの範囲を表示)
    Show Collider Sleep(コライダーが寝てるかを表示)
    Show Collider Contacts(当たり判定の接点を表示)にチェックを入れます。

f:id:tsubaki_t1:20160809231108j:plain

なお、Gizmosの線が細いので、背景と一緒に表示するようなケースの場合かなり見分けにくくなるかもしれません。その辺りは、スリープやコライダーの色を調整する等で何とかする必要がある気がします。

f:id:tsubaki_t1:20160809232005j:plain

物理演算は積極的に寝かせるが吉

物理演算的に言えばこのスリープ処理は非常に重要で、例えばBox(四角形)とCircle(円)の当たり判定を単純に比較した場合、スリープしやすいBoxの方が平均的なFPSは稼ぎやすかったりします。

f:id:tsubaki_t1:20160809231247g:plain
逆に、寝るタイミングが寝ない・もしくはスリープしない物理演算の場合、BOXよりCircleの方が計算が単純な分高速で動作します。その辺りは、当たり判定の形状に合わせるだけでなく、用途によって使い分けた方が良さそうです。

Rigibody(3D)については

テスト版の機能として公開されてるみたいです。

blogs.unity3d.com

関連

tsubakit1.hateblo.jp

【Unity】Animatorで動かすオブジェクトをスクリプトでも動かす際の注意

f:id:tsubaki_t1:20160809002457g:plain

少し面白い挙動について調査したので、メモ。

(前提)モデルにスクリプトで回転を加える

単純にモデルに回転を加えたい時、下の様なコードを記述すれば回ります。現在のローテーションに加算して回転を加えている訳です。

using UnityEngine;
using System.Collections;

public class Rotation : MonoBehaviour {

	void LateUpdate () 
	{
		transform.rotation *= Quaternion.AngleAxis (2, Vector3.up);
	}
}

f:id:tsubaki_t1:20160809003018g:plain

(前提)さらに色を変えるのみのアニメーションを加える

上記の回転してるオブジェクトに色を変更するアニメーション「Color」を追加します。Colorには座標を移動する系のカーブは持たせません。

f:id:tsubaki_t1:20160809003447j:plain

f:id:tsubaki_t1:20160809003453j:plain

これを設定すると、回転しながら色が変わるようになります。

f:id:tsubaki_t1:20160809002457g:plain

(問題)回転を設定したアニメーションを加えると動かなくなる

上記の回転しつつ色が変わるオブジェクトに、回転するアニメーションを「Rotation」を加えます。想定としては、基本的にはアニメーションで動いてもらい、必要な時にはスクリプトで制御する流れです。

f:id:tsubaki_t1:20160809004139j:plain

f:id:tsubaki_t1:20160809004130j:plain

「Rotation」のアニメーションを追加すると、Colorアニメーションを再生している時でもスクリプトによる回転が効かなくなります。

f:id:tsubaki_t1:20160809004343g:plain

位置が変化するアニメーションが設定されているという事

今回の問題は「Rotation」アニメーションに位置や回転を変化させるアニメーションが設定されていたという事がトリガーになっているみたいです。

アニメーションで動くオブジェクトは、基本的に該当の角度・位置へ毎フレーム再設定しています。

Mecanimの場合は面倒くさいことに、AnimationControllerの中にrotationもしくはpositionを動かしているカーブが含まれている場合、今回作成したColorのような「回転処理を設定していないアニメーション」に対しても「初期値に戻すアニメーション」を設定するみたいです。

アニメーションしているのがRootMotionの場合、回転や位置調整を設定したアニメーションクリップが含まれていると「Root position or rotation are controlled by curves」という表示がAnimatorに加わります。

f:id:tsubaki_t1:20160809005524j:plain

ただ、RootMotoin以外でアニメーションしている場合はこの表示が加えられないので注意が必要です。

 

例えば2Dゲームで一部カットシーン的に動かしたりする場合に時々起こるんじゃないかなと思わなくもないので、その辺り注意する必要があるかもしれません。

f:id:tsubaki_t1:20160809013146g:plain

回転しなくなる現象の対策

対策ですが、「初期値に戻すアニメーションが必ず効いている」事を前提にスクリプト自体を少し考え直せば良いです。
但し「アニメーションで動かしていない」部分は加算すると大参事になるかもしれないので、注意が必要です。再設定の現象が起こるのは、あくまでAnimationで設定しているパラメータです。

using UnityEngine;
using System.Collections;

public class Rotation : MonoBehaviour {

	Quaternion r = Quaternion.identity;

	void LateUpdate () 
	{
		r *= Quaternion.AngleAxis (2, Vector3.up);
		transform.rotation*= r;
	}
}

 他には、アニメーションしているのがroot motoinの場合、Apply Root Motoinにチェックを入れた場合「初期位置に戻す処理」が設定されなくなったような気がします。また、良いのか悪いのか判断に迷いますが、Animationコンポーネントではこういった事は起こりませんでした。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

【Unity】Navmeshで取得した経路をApplyRootMotionで移動させる手っ取り早い方法

f:id:tsubaki_t1:20160807232232g:plain

今回はNavmeshとApplyRootMotionを組み合わせて動かす手っ取り早い方法についてです。

 

なお、逆のパターン(Nameshを主としてAnimatorを動かす)場合は、こちら。

tsubakit1.hateblo.jp

ApplyRootMotionとNavmesh

UnityのアニメーションシステムであるMecanimには、ApplyRootMotoinという機能があります。これは、アニメーションの移動値を元にキャラクターの座標を移動させるという物です。上手く使うことで、キャラクターを走らせる際「蹴り始めは加速」「着地時に減速」といった、緩急のある移動が可能になります。

http://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20150726/20150726025410.gif

http://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20150726/20150726025200.gif

一方Navmeshはと言えば、最大スピードと加速等で移動し、目的地付近になると減速します。そのため、速度とモーションの速度が一致していない場合、足踏みしているような不自然な挙動になる事があります。

f:id:tsubaki_t1:20160807235045g:plain

要するにAniamtorとNavmesh双方ともに動きを管理している訳です。今回はその2つを組み合わせて使ってみます。

今回の目的はNavmeshではなくAnimatorのApplyRootMotoin主導で動かす事です。なので、Navmeshの移動自体を止めてしまいます。

NavmeshAgentを設定したオブジェクトの移動を止めるには、updatePosition = false;を使用します。

f:id:tsubaki_t1:20160807235659g:plain

但し、上のGifアニメのようにNavmeshAgent自体の動きが止まる訳ではない点に注意する必要があります。

NavmeshAgentと動きが一致していないと曲がって欲しい所で曲がってくれないので、NavmeshAgentの位置を、agent.nextPosition = transform.position;のような形で現在の座標に設定します。

後は、ApplyRootMotionを設定したアニメーションで移動させてやれば、ある程度パスに沿ってキャラクターが移動してくれます。

 終了距離が近づいてきたら、動かないアニメーションに切り替える

キャラクターが移動してくれるのは良いのですが、Navmeshによる移動系の処理を殆ど切っている為、AutoBraking等の機能によりブレーキを踏んでくれません。

なので、停止するアニメーションを設定します。まずは下のようにIsStopならIdleする、といった設定にします。

f:id:tsubaki_t1:20160808001156j:plain

後は、remainingDistance (目標地点までの距離)がstoppingDistance(停止する位置)より近くなっていたら、アニメーションをIdleに切り替えます。

animator.SetBool ("IsStop", agent.remainingDistance < agent.stoppingDistance);

これで、目標地点に近づいたら停止します。

一応、NavmeshAgentのAuto Breaking設定は使わないのでチェックボックスを外しておくが吉です。

要するに

要するに、

  • 前進後退等はApplyRootMotoin
  • 旋回はNavmeshAgentにお任せ
  • 停止はNavmeshAgentの設定を元に、ApplyRootMotionで動かないキャラに切替

みたいな感じです。

gist.github.com

旋回部分もApplyRootMotoinでやる場合、NavmeshAgentのupdateRotation = false;で回転を制限して、その上で agent.desiredVelocityを見て曲がるアニメーションを設定すれば良いかなと。

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp