Toybox
VRCWorld製作者のJason Lim氏が公開している、
非常に高度で興味深い内容が含まれるUnityPackage「Toybox」の解説ページです。
編集時Ver.1.2
このUnityPackageの使い方
本ページの内容は、Toyboxに含まれているシーンを読み込んだうえで確認することを前提に説明していくため、
始めて使おうとする方は下記の手順に従い新しいプロジェクトを作成してみてください。
1. Unityエディタで新しくプロジェクトを作成
2. VRC_SDKおよびToyboxのUnityPackageをインポート
3. Standard Assetsをインポート(重要)
4. /Assets/ToyboxにあるToybox.unityをシーンとして読み込む
ご自身のプロジェクトに組み込む場合、一通り内容の確認が出来た後に順次実装していくことをオススメします。
uGUIイベント
Prefab内で利用されているuGUIイベントに関してはこちらをご覧ください→uGUI
Toybox
プレイヤーに追尾するクリスタルやNPC、ミニマップの表示システム、HPバーシステム等。
PlayerTracking
ワールド内でプレイヤーに追尾するPlayerHandleの設定部。
- CombatSystemのVisualDamagePrefabにRefHandleを設定。
プレイヤーCameraの位置にRefHandleのコピーを作成している。
- 位置、回転を0、スケールを1に設定したAnimation[LockTransform]を実行し続ける事でPrefabの巨大化を回避していると思われる。
- Setupにて、uGUIイベントを実行するオブジェクトのAnimator.fireEvents関数をFalse実行。
原理は不明だが、これによってワールド内のRefHandleのみアニメーションイベントが無効化され、
コピーされたRefHandleのみがuGUIイベントを実行できるようになる。
- AcquireHandleのuGUIイベントでRefHandleをPlayerHandleの親に設定。
これによってPrefabからワールド内オブジェクトへの連結を行っている。
- 他のワールドに移動した際にRefHandleを消去するため、RefHandleにはTimedObjectDestructorがセットされている。
これはワールド内オブジェクトのHandleLifelineが連続的に、PlayerHandle→RefHandleを経由してTimeoutResetのActivateTriggerを起動する事で、Destroyまでの時間がリセットされ維持されている。
ワールド移動の際にはHandleLifelineからの信号が途絶えるためRefHandleはDestroyされる事になる。
PropSpawner
プレイヤーに追従するクリスタル。ワールド上のボタンで生成、消去、色変更ができる。
- この項目では、SpawnedItemがSpawnObjectアクションによって生成されているが、
Projectから指定する方法だとワールド内のPlayerHandleを対象に取れないため、
SpawnObjectのPrefabにワールド内のSpawnedItemを指定し、そのコピーの生成を行っている。
そのままではPrefabの欄にはアタッチできないが、DebugモードでSpawnObjectのParameterStringにオブジェクト名を入力する事で直接指定する事が出来る。
(Debugモードにはインスペクタ右上のメニューから変更できる)
- プレイヤーが入室すると、そのプレイヤーのLocalでTriggerOnceがアクティブ化し、OnTimerによって1秒後に、
Spawnerオブジェクトのクリスタル生成処理である、CustomトリガーSpawnOwnedが発動する。
- SpawnOwnedではLocal上でのみコピー元のSpawnedItemのOwnerEventsをアクティブ化してから、
SpawnObjectすることで、
オーナーのLocalでのみOwnerEventsがアクティブ化した状態でSpawnedItemのコピーを生成している。
(Spawnしたオブジェクトのオーナーシップは、SpawnObjectを実行したプレイヤーになる)
- SpawnedItemは生成されると、OnSpawnによってアクティブ化される。
すでにOwnerEventsがアクティブ化しているオーナー上でのみ、
SetupのOnEnableが発動しFloatCrystalのFollowTargetが有効になりPlayerHandleに追尾するようになる。
- OwnershipCheckはオーナー退出時にクリスタルを消去する処理を行っている。
Animatorによって、ClaimedパラメータがFalseになってから3秒(厳密に言えば1/0.33秒)以内にTrueにならない限り、
CustomトリガーDestroyをアニメーションイベントから直接発火している。
オーナー上においては、ClaimOwnershipによって、
クリスタルがアクティブ化したタイミングでCustomトリガーClaimOwnerが実行され、
OwnerパラメータをTrueにする事でこの処理が行われないようになっている。
- プレイヤーが退出すると、PlayerLeftでClaimedパラメータがFalseになるが、
同時にClaimOwnerShipからCustomトリガーClaimを通してClaimedがTrueになり、
それをAlwaysUnbufferedで他者に与えることでオーナーがいる限りクリスタルは維持される。
ClaimOwnerShipはOwnerEventsの子要素のため、
それがアクティブ化しているオーナーからしかClaimが発動される事は無い。
- OwnershipCheckのCustomトリガーDestroyが発動すると、DespawnのCustomトリガーに繋がり、
SendRPCからSpawnedItemのReapObject関数が呼ばれ、SpawnedItemは消去される。
ReapObject関数はObjectSyncコンポーネントに所属し、自身がSpawnしたオブジェクトの場合、それを消去する。
- ボタン入力をSpawnedItemに伝達する機構はRelayTriggersが担っており、
子オブジェクトのAnimatorのTriggerパラメータがトリガーされるとAnimationが実行されるが、
これの遷移時間はInfinityとなっており、遷移の終了条件をNextStateにすることで、
ステートがWaitに辿り着くまでのほんの一瞬の間だけ、Animationが維持されコライダが有効になる。
(この仕様の詳しい条件はHealthSystemにて後述。)
これをFollowTargetで同位置に配置されたExternalTriggersの子オブジェクトが
OnEnterTriggerで検知する事で伝達を行っている。
- FloatCrystalではAnimationからアニメーションイベントで内部関数が連続実行されている。
同期の際のスムージングに関係するものかもしれない。
MapSystem
ワールド上のプレイヤー、NPCの位置をミニマップ上のマーカーに変換して表示する。
- Minimapの子要素であるマーカーを、プレイヤーやNPCに追尾するAnchorオブジェクトに
Suspensionで連動させる事で移動量をローカルに変換して動かしている。
- AnchorPlayerの親であるSpawnedItem2の生成、消去はPropSpawnerの項目を参照。
これはPropSpawnerのクリスタルがPlayerAnchorに変わった物に近い。
- SpawnedItem2生成の際、OwnerEventsのSetupからCustomトリガーを呼び、
MarkerPairのIsOwnerパラメータをTrueにしている。
これによりオーナー上でのみ、AnimationによってPlayerAnchorのPlayerHandleへのFollowTargetが有効化、
PlayerMarkerのVisualのMaterialを緑に変更している。
- 全プレイヤー共通の処理として、PublicEventsのSetupでuGUIイベントを起動し、
PlayerMarkerの親をMinimapの子要素Markersに設定している。
親を移した際、PlayerMarkerの位置をMinimapの表面にリセットするため、
位置・回転・スケールをリセットするAnimationをSample関数で一度だけ実行してから
Suspensionを有効化している。
- Despawn時にはPlayerMarkerはSpawnedItem2の子要素から外れているため、忘れずDestroyする。
HealthSystem
プレイヤーに追従するヘルスバー。ボタンで増減でき、0になるとプレイヤーをリスポーンさせる。
- ヘルスバーの生成、消去、ボタンによる入力の伝達はPropSpawnerを参照。
- オーナー上でのみ、生成時にSetupにてPlayerHealthのIsOwnerパラメータをTrueに、HealEventsをアクティブ化している。
- NetworkEventsのExternalTriggersにて、伝達されたボタン入力の各処理を実装している。
また、ヘルスが0になった場合、OnDeathオブジェクトのCustomトリガーが呼ばれ、テレポートと全回復処理を行う。
- ヘルスの同期は、ヘルスが25%変化する毎にオーナー上でのみ起動するHealthEventsを全員に伝達する事で取っている。それより細かい数字はオーナー上でしか反映されていない。
- ヘルスの増減や同期イベントの実行、ヘルスバーのスケール変更等、
全ての処理はHealthPool100のAnimatorによって行われている。
- ヘルス減少の際のイベントを追加したい場合は、HealthPool100フォルダのAnimation、
CustomHealthBarFillを変更すればいい。Healthパラメータが対応する位置にキーを追加しよう。
HealthPool100の解説
NPCs
NPCSimple
接近したプレイヤーの方向を向くNPC。新たなプレイヤーが接近した場合はそちらの方向を向く。
追尾関係の機構
- NPCSimpleオブジェクトにはNPCの追尾のオンオフを切り替えるNPCAnimatorが設定されている。
パラメータのAttentionは『NPCが誰かを追尾しているか』、IsAttentionTargetは『ローカル上でそのプレイヤーを追尾しているか』のフラグである。
IsAttentionTargetがオンになる事で、プレイヤーへの追尾が開始される。
- 追尾にはAttentionTargetオブジェクトが使用されており、これのFollowTargetが有効になることでPlayerHandleの位置に移動する。
- NPCの頭はHeadAnchorオブジェクトからJointで繋がれており、
これがLookatTargetでAttentionTargetの方向を向くことで首を動かしている。
ターゲットがいない場合、AttentionTargetはRestTargetのJointで引っ張られてNPCの正面に戻る。
NPCsimpleのAttentionがオンの時、RestTargetのJointのLimit値が3.402823e+38(最大値)に変更されてることに注意。
ターゲット変更関係の機構
- NPCAvatarのAttentionZoneが検知範囲であり、ローカルプレイヤーがトリガーすると、
『Local』(AttentionMediatorの子オブジェクト)のCustomトリガーが呼ばれる。
また、再検索用のフラグであるIsNearbyパラメータがTrueになる。
- 『Local』のCustomトリガーではトリガーしたプレイヤーに3つの処理が行われる。
- EventsとAttentionTargetのオーナーシップの取得
- 全てのプレイヤーのIsAttentionTargetをFalseに。その後全てのプレイヤーのAttentionをTrueに
- ローカルプレイヤーのIsAttentionTargetをTrueに
- ※オーナーシップの取得についてはVRC_ObjectSyncを参照
- この処理によってAttentionTargetがObjectSyncで同期するようになり、NPCの動きが同期される。
- プレイヤーがAttentionZoneの検知範囲から外れた場合、『UnbufferedOwner』のCustomトリガーが呼ばれる。
それと共にIsNearbyのパラメータがFalseとなる。
- 『UnbufferedOwner』のCustomトリガーでは、まずBroadCastTypeがOwnerUnbufferedのCustomトリガーを経由する事でEventsオブジェクトのオーナー(ターゲットされているプレイヤー)以外はトリガーを引けないようになっている。
トリガーが引かれた場合、全てのプレイヤーのAttention、IsAttentionTargetをFalse、
そしてAttentionMediatorのCaptureNearbyパラメーターをトリガーする。
- AttentionMediatorは再抽選処理であり、CaptureNearbyがトリガーされた際、
IsNearbyがTrueのプレイヤーに『Local』のCustomトリガーを引かせている。
この際のCustomトリガーの起動は前述したuGUIイベントの起動と同じ方法で行われている。
- OnAttentionOnとOnAttentionOffは処理が実装されてないので、NPCが反応した際にやりたい処理を入れよう。
吹き出しを出したりすると面白いと思います。
NPCPathFollow
NPCSimpleの機能に加え、Pathに沿って自動的に歩行を行うNPC。
- NPCの移動はUnityの標準機能であるNavMeshAgentでNavTargetオブジェクトまでの経路を計算し、
AICharacterControl、ThirdPersonCharacterを通すことで移動を行っている。
Pathの変更や移動の停止は移動先であるNavTargetを逐次移動させる事で行っている。
NavTarget(移動先)操作関係の機構
- 移動状態と停止状態の遷移はNPCSimpleでは実装されていなかったOnAttentionOnと、OnAttentionOffで行われ
AttentionパラメータがTrue、Falseになった際に各自アクティブ化され呼ばれる。
ここでNavSystemのPausePathパラメータを切り替えする事でPause状態のオンオフを行っている。
- NavSystemではAnimatorによって二つの処理を行っている。
一つはPathの変更で、2秒(再生速度0.2で実際は10秒)毎にNavPathの4つのWayPointのアクティブ状態を順番に切り替えている。
この際、途中参加のプレイヤーにもAnimationの状態を同期するためVRC_SyncAnimationコンポーネントが使用されている。
- もう一つはPause処理であり、PausePathがTrueになるとPathの切り替えAnimationの再生速度を0にして停止させ、
後述するPathTargetとRestTargetの、CtrlPivotのジョイントLimit値を変更している。
- 各WayPointは順番にアクティブ化し、現在のPathの位置を表すPathTargetオブジェクトをジョイントで引き付けている。
しかし、非アクティブからアクティブになったジョイントは、物理状態の変化が発生しない限り効果を発揮しない仕様があるため、Animationで常時僅かに回転させることでこれを回避している。
- PathTargetの子であるCtrlPivotが、Pause状態で無い限りNavTargetを引き付け、
現在のPathの位置にNPCを誘導する。
- RestTargetは常にNPCの位置にFollowTargetで移動し、CtrlPivotがNavTargetをジョイントで繋いでいるが、普段はCtrlPivotのジョイントLimit値が3.402823e+38(≒∞)に設定されているため、NavTargetに干渉する事は無い。
Pause状態になると、NavSystemによってPathTargetとRestTargetのジョイントLimit値が入れ替えられ、NPC自身の位置が目的地となる。
NPCの移動、位置同期関係の機構
- 前提として、NPCの移動ではオーナーシップを取っておらず、ワールドのMaster基準で同期を行っていると思われる。
ObjectSyncによって同期を行っているのは、NavTarget,SyncAgent,FloatCrystalである。
- NPCAvatarのAICharacterControl、ThirdPersonCharacterで移動を、
子オブジェクトのNavAgentのNavMeshAgentで経路探査を行っている。
恐らくNavMeshAgentの探査を無効にするAnimationが設定されているが、これはPrefab内で使用されていない。
(AICharacterControlとNavMeshAgentを別オブジェクトにする方法はhttps://twitter.com/JasonL663/status/986998395632926722参照)
- SyncAgentではSetupによって開始時起動されるAnimationから、
恐らくVRC_ObjectSyncの内部関数が連続実行されているが、詳細は不明。同期関連だと思われる。
- CopyTransformでは途中参加のプレイヤーにNPCの位置の同期を行っている。
入室時、uGUIイベントからVRC_SceneResetPosition.ResetPosition関数を呼び出し、
NPCAvatarの位置を、同期しているSyncAgentの位置へ。二つは親子関係のためSyncAgentの位置がずれるのを修正するため、さらにSyncAgentの位置をNPCAvatarの位置へ設定しなおしている。
- ワールドの下方には巨大なコライダが敷かれており、それにRespawnTriggerが接触すると(=NPCが落下すると)、
ResetPositionのuGUIイベントが呼ばれ、位置リセットが行われる。
- SyncAgentをNavTargetの位置に移動するため、
SyncAgentのFollowTarget.LateUpdate関数を一回だけ直接実行している。
Private関数はuGUIイベントのリストには出現しないが、UnityのDebugモード(インスペクタ右上のメニュー)なら直接指定することができる。
- 後はCopyTransformの処理と同じように、SyncAgentとNPCAvatarの位置をリセットしている。
応用例
とりあえずメモで
- https://twitter.com/Melnus_/status/988783758051393536
- https://twitter.com/noriro_08/status/1192324725554286593
ToyboxV2
フックショット、バトルディスク、スライダーコントローラー、オブジェクトプール等
前のバージョンに比べてPlayerのトラッキング方法がCanvasによるものに変更されている。
- V2におけるNPCが顔を向いてくれる機能のメモ
スライダーコントローラー
- hatsucaさんによる仕様概要
- スライダーの挙動がVRとデスクトップで異なるらしい
オブジェクトプール
BigSmallSpace
BigToSmall / SmallToBig
- Suspensionを参照。
コメント・情報提供
カテゴリ・タグ: カテゴリ-Tips
- 最終更新:2019-11-07 16:14:58