はじめに
一行書く。タグを付ける。何かを表明する。そして行を別の場所へ渡す。
インストール
まず、VCC(VRChat Creator Companion)でロギングパッケージを Unity プロジェクトに追加します。プロジェクトの vccproject.json を開いてパッケージを追加するか、VCC インターフェースを使って直接インストールしてください。
インストール後、パッケージはプロジェクトで利用可能になり、以下のロギング API を使い始めることができます。
一行書く
名前空間を足して、書くだけです。シーンに置くものも、繋ぐものもありません。
using Udonite.Logging;
public class Door : UdoniteBehaviour
{
public override void Interact()
{
Log.Info("opened");
}
}
[Door.Interact:8] opened
Log.Warning と Log.Error は、同じ一行を Console の別のチャンネルに書きます。
型名はファイル名から取っています。 実行時に Udon へ「囲んでいる型」を尋ねる方法はありません。そして Unity は型名とファイル名が一致する behaviour しか認識しないので、ログを書く behaviour については両者が同じ文字列です。別の名前のクラスから呼ぶと、そのクラス名ではなくファイル名が出ます。
関係する行にタグを付ける
タグは同じ話題の行をまとめるもので、呼び出し位置より前に出ます。
Log.Info("hit for " + damage, "combat");
[combat] [Weapon.Fire:22] hit for 3
タグは行の中のただの文字なので、Console の検索欄でそのまま絞り込めます。
何かを表明する
Log.Assert は条件が偽のときだけ報告し、その条件のソースをそのまま引用します。
Log.Assert(owner != null);
Log.Assert(players.Length > 0);
[assertion failed] [Room.Join:31] owner != null
[assertion failed] [Room.Join:32] players.Length > 0
この引用こそが、if と Error を書く代わりにこれを使う理由です。何かが偽だったとだけ言うのではなく、何が偽だったのかを名指しします。しかも後で条件を書き換えても、言うことは正しいままです。
条件だけでは説明にならない場面では、メッセージが引用の代わりに入ります。
Log.Assert(state == Ready, "部屋の読み込みが終わる前に join ハンドラが走った");
報告して戻るだけで、決して throw しません。 Udon では例外が起きるとその behaviour はセッションの残りのあいだ停止します。診断のつもりが、ワールドにいる全員にとっての障害になってしまいます。
行を別の場所へ渡す
ワールド内のパネル、リングバッファ、クライアントの外へ送る文字列。行き先がある一行のために、Log.Entry は書く代わりに部品を返します。
LogEntry entry = Log.Entry("hit for " + damage, LogLevel.Warning, "combat");
panel.Show(entry.Message, entry.Tag, entry.Time);
Log.Write(entry);
Message、Text、Tag、Type、Member、Line、Level、Time が別々のフィールドになっています。2 メートル先のパネルが欲しがる部分と、コンソールが欲しがる部分は違うからです。
ふつうの呼び出しをタダのままにするために、これは別の呼び出しです。 Entry は毎回オブジェクトを確保します。Update からログを書くワールドが、誰も読まないオブジェクトの代金を払う理由はありません。Log.Info とその仲間は何も返しません。
何がかかるのか
ログの呼び出しは文字列を組み立てますし、ワールドは 10 秒の VM 予算の下で動いています。人間の速さなら無視できますが、ホットループの中では本物のコストです。毎フレーム全プレイヤー分走るコードには置かないでください。
呼び出しを消す以外に、ログを止める方法はありません。本来それをやるべき仕組みは [Conditional] で、呼び出しごと引数ごと消してしまうものですが、Udonite はまだこれを尊重しません。