Search Unity

Unity 5.1 では新しくUnityにアサート機能を提供する Assertion ライブラリが提供されました。この記事ではアサートとは何か、この機能をどう使えばゲーム実行時のエラーからより多くの情報を取得して問題解決に繋げられるかをお話しします。

アサートって何?なぜアサートを使った方がよいのか

アサートとはザックリ言うと特定の条件が成立しているかどうかをチェックするための機能です。もし条件が成立(trueを返)していれば、アサート関数はなにもせずにゲームは通常どおり進行します。しかし何かが起きて期待している条件が成立しなくなると、アサート関数はメッセージとコールスタックを表示します。え、ピンと来ない?それじゃあ、さっそく具体例を見てみましょう:

この例では、参照しているゲームオブジェクト(GameObject)がアクティブでなかった時に、あらかじめ設定しておいたエラーメッセージ“Player GameObject is not active”と発生時のコールスタックがエラーログメッセージとして出力されます。

Assertion message

多くの開発者はアサートをユニットテストで学んでいます。単体テストを記述するための一般的な方法である Arrange-Act-Assert (AAA) パターンを使ったユニットテストの実装では、アサートの利用は最後の部分である結果の比較に利用します。しかしアサートは短にテストのための機能ではありません。実際のゲームのコードにも使用する事で、ゲームの実行時に何かおかしな事が起きたら早めに分かるようにすることが可能です。とはいえ、すべてのアサート機能がゲームのコードに使われるべき、というわけでもないのです。

ユニットテスト・フレームワーク由来のアサート機能

アサートにはじめて出会ったのがユニットテスト・フレームワークだという人は結構多いのではないでしょうか。ユニットテスト・フレームワークというのは、たとえばNUnitなどです。NUnitは充実した機能を持ちつつも実戦で鍛えられたアサート機能のライブラリを備えており、それを使ってテストを書くことが出来る優れものです。「え、じゃあもうゲーム開発でもそれを使えばいいじゃない?」…そう思われるのは自然な流れですが、NUnitが適切でない理由がいくつかあります。一番大きな理由を一言でまとめると「そういう目的で作られてないから!」この一点に尽きます。

NUnitのアサート機能はさまざまな事柄をテスト可能にしてくれます。単純な等値性の比較から、もっと複雑な処理のはてに例外を投げるようなものまで多岐に渡ります。これはありがたい機能なのですが、実際のゲームコードで利用するには少々遅すぎるのです。ゲームコード用に使えるアサートはできる限り無駄を廃したコードで、実行時の負荷を極限まで下げる必要があります。UnityのAssertionライブラリは不要なメモリ確保や無駄な処理を一切行わないよう注意深く実装されています。

もう一つ、ゲーム用のAssertionライブラリは、そのメソッド呼び出しをリリースビルドの時に完全に除去することが可能になっているべきです。アサート機能は開発中は便利ですが、例え(アサートに失敗して)問題を捕捉してもエンドユーザーには何の価値もありません。ですので、最終的な製品向けプログラムのビルドではアサート処理は取っ払ってしまいたいと思われるでしょう。論理的には全部のアサート処理をコメントアウトする方法もあるでしょうが、あまり頭のいい方法ではありませんよね。幸運な事に.NETは、こういうときのために条件コンパイルのメカニズムを有しています。AssertionライブラリはConditionalアトリビュートをもっているので、アサート処理は開発用(development)ビルドでのみ含まれるようになっています。しかしながら、もし望むのであれば、コンパイルオプションを明示的に設定することで強制的にAssertionを有効にすることも可能です。

最後に、通常のユニットテスト用ライブラリのアサート処理は例外を投げることを前提に設計されています。例外が投げられると、当然ですが処理の流れが変わってしまいます。これはゲームコードからすると望ましい状態ではありません。代わりに、AssertionライブラリはUnityのログシステムと統合されており、メッセージは例外ではなくログに記録される形になります。この変更によりライブラリを全てのプラットフォームで幅広い用途で利用出来るようになります。さらにこの方針は例外をサポートしていないAOT(Ahead-Of-Time Compile, 事前コンパイル方式)ベースのプラットフォームでも利用出来るメリットがあります。

UnityのAssertionライブラリに含まれるもの

UnityのAssertionライブラリはいくつかの型に特化した比較メソッドと一般的な等値性比較クラス(Comparer)を有しています。いくつかの機能を紹介しますと:

  • AreEqual – 一般的な等値性のアサートに使われるメソッドです。デフォルトの等値性比較用Comparerで比較されます。
  • AreApproximatelyEqual – 計算エラーの揺らぎを許容した近似比較メソッドです。小さな計算誤差の出る浮動小数点数の比較などに便利です。
  • IsTrue – 手早く簡単なブーリアン比較に使えるメソッドです。

その他、ライブラリの全機能はAssertのドキュメントを参照してください。

このライブラリのいいところは、Unity Test Toolsとの完全な互換性を保っていることです。特に追加の努力をなにもしなくても、アサートはUnity Test Toolsから呼ばれた結合テストで適切に失敗します。

Assertionライブラリを拡張する

Assertion機能をぞんぶんに活用したいと思う時、これを拡張したいと思うのは自然なことです。簡単で機能的な方法として、AssertionライブラリのAreEqualメソッドは特定の型について開発者が独自に比較クラス(Compararer)を実装して渡すことが出来るようになっています。比較機能を新たに実装する場合は IEqualityComparer インターフェイスを利用します。

たとえば、ライブラリは浮動小数点数を比較するために内部的にIEqualityComparer< float>を実装した FloatComparer というクラスを作成し、AreApproximatelyEqual で利用しています。

まとめ

Assertionライブラリはすぐに利用可能ですので、これを活用してゲームのコード上の意図しない状態を発見しやすくして、あなたのコードをバグから守りましょう!Assertionライブラリの機能は今後も改善していきますが、みなさんからのフィードバックやご提案も積極的におまちしています!

コメント受付を終了しました。

  1. Thank you for this, this is good stuff and I hope this aspect of Unity continues to be improved.

    A suggestion I have is as follows:
    – I am using the UnityVS plugin, so I am happily debugging my C# Unity code in VS 2015.
    – If an Assert method fails (Assert.IsTrue( meaningOfLife == 42);) then can the assert cause the VS to breakpoint, stopping at the exact line where the Assert() method is?

    Or if the above is possible now, I’d love to know.

    Thanks.

  2. Piotr Korzuszek

    9月 1, 2015 9:14 am

    Hi Tomek,

    Can I make a feature request? It would be really cool to make Assert function calls to take UnityEngine.Object context as the last argument just like Debug.Log*() functions. By providing a context the developer can click on the log message and he can quickly identify the faulty object. This can be extremely useful when there are many scene objects with the same script and only part of them are failing the asserts.

    Thank you for your consideration!
    Piotr

  3. Hey this library seems very useful and it could be even more if it was compatible with Resharper and its Annotations. As you even include Resharper Annotation dll now with Unity, it would be great if Unity3D code made use of it !

  4. Peter Friedland

    8月 28, 2015 2:12 pm

    I really appreceate this as well. Do you plan to make assertions internally as well? Will I be able to disable assertions separatly from debug mode?

    hopefully Assertions doesn’t affect performance so much!

  5. Its cool!
    But the reason I dont use tests in unity,
    Is because it doesnt show code coverage, and its hard to follow my tests this way..

  6. Thanks Tomek for the down-to-earth explanation, this is really useful. I hope Unity Test Tools get integrated natively in Unity.
    I however can’t help but notice that Unity is getting way more namespaces lately.

  7. Scott Richmond

    8月 25, 2015 5:23 pm

    It’s great to see some love given to the finer areas of programming with Unity, thanks.