導入
Rigidbodyとは
UnityにはRigidbody
と言われる物理演算等をしてくれるコンポーネントが有ります。
Unityの物理演算を利用しなくとも、Unityの当たり判定機能を使う為にもこのRigidbody
コンポーネントを少なくとも片方のオブジェクトが持っている必要があるので、Unityを使ったことがある人はおよそほとんどが使ったことがあるであろうコンポーネントです。
Sleepという機能
普通に使っている分には非常に便利なのですが、ちょっとした落とし穴があります。それはスリープ(Sleep)という機能です。
リジッドボディが決められた最低直線、または、回転スピードよりも動きが遅いとき、物理エンジンはそれが休止していると仮定します。これが発生すると、ゲームオブジェクトは衝突されるか力が与えられない限り再び動きません。そのため、それは「スリープ」モードに設定されます。この最適化により、次にリジッドボディが「起きる」 (これは、再動作のこと) までプロセッサーの時間が更新に使われることがありません。 公式より引用
つまり、Rigidbody
は決められた速度(速度、というのは厳密ではない)よりも遅く移動している等の場合、処理軽減のためスリープモードとなり、力を与えられない限り物理演算を行わず動かなくなります。
一見それほど問題はなさそうですが、力を与えられない変更、例えばそのスリープとなっているオブジェクトの下の床が消えたといった場合に、オブジェクトは物理演算を行わないのでそのまま宙に浮いたままになったりします。
問題
さて、今回は「OnCollisionStay
でプレイヤーの接地判定を取り、その判定がある時だけジャンプが可能になる」という実装をしようとしていたのですが、プレイヤーが止まるとスリープになってしまいOnCollisionStay
が呼ばれなくてうまくいきませんでした。じゃあスリープしないようにしよう!となったわけです。
スリープをしないようにしよう!
SleepingModeがない!!
さてここまではよくある話なのですが、僕はここで徐にインスペクタでSleepingMode
(スリープする条件を指定する項目)をNever Sleep
(スリープを無効にする)にしようとしました。処理が重くなるので本来推奨されないのですが、面倒なのでそもそもスリープしなくしてしまいたかったのです。
しかし、インスペクタを見てもSleepingMode
がありません。
以前に使った事があったので(白目)おかしいなと思いながら調べてみるとどうやらRigidbody
にそんなものはないらしい。
え?うそ?前使ったよ?バージョンアップでなくなったの?と思って「Rigidbody never sleep」とかで検索。
結論から言うとRigidbodyにはSleepingModeはなく、Rigidbody2Dのみにあるということらしいです。
なんじゃそりゃ、という感じなのですが、3Dの処理はやはり大変なので簡単にNever Sleep
にはされたくないんでしょうかね。
それでもNever Sleepにしたい!!
でも僕はSleepしないようにしたい…
調べるとWakeup
関数(強制的にスリープモードを解除する関数)を毎フレーム呼んだり、IsSleeping
関数(スリープモードかどうかを返す関数)で判定して、スリープモードの時はWakeUp
関数を呼んだりしてるのを見つけたんですが、毎フレーム呼んだりするのはなんとなく抵抗が…
もう少しスマートに出来ないかなと思って公式マニュアルをみていた所、Rigidbody
にsleepThreshold
というメンバ変数を発見。
スリープをするのは決められた速度(上述の通り厳密ではない)よりも遅く移動している時と言いましたが、大雑把に言うとこの変数はそのスリープをする決められた速度を規定する変数です。
よって例えば、sleepThreshold
を-1とかにすると、「速度が-1より小さくなった時にスリープする」=「速度は0以上なのでスリープしない」となって、Start()
内で一回rigid_body.sleepThreshold=-1;
みたいな感じで書いてやればずっとスリープしなくなるわけです。
取り敢えずこんな方法でスリープを回避できるようにはなったんですが、急に思いついた方法(かつ正確に理解できているかわからない)なのでどういう副作用があるかわからないというのと、スリープを切るともちろん処理は重くなるので、使用する場合は自己責任かつあまり使わないようにした方がいいかな、と思います。
面倒なのでプレイヤーだけなら大丈夫かな?とか思って僕は使っちゃいますが。