概要
ワールド内にただ置かれているだけでなく、プレイヤーが実際に手に取って、振ったり、使ったり、投げたりできるピックアップオブジェクトは、ワールドとのインタラクションを格段に深める重要な要素です。懐中電灯、ボール、武器、食べ物など、その応用範囲は無限大です。
UdonSharpでピックアップオブジェクトを実装するには、VRChat SDKが提供するVRCPickupコンポーネントと、それに関連するUdonSharpのイベントを利用します。
本 記事では、基本的なピックアップオブジェクトの設定方法から、掴んだり離したりした際のイベント処理、そしてオブジェクトの状態を同期させる方法までを解説します。
ステップ1: VRCPickupコンポーネントの設定
まず、プレイヤーが掴めるようにしたいオブジェクトに、必要なコンポーネントを追加します。
-
オブジェクトの準備: シーン内に、プレイヤーに掴ませたい3Dモデル(例: Sphere、Cube)を配置します。
-
Colliderの追加: オブジェクトにBox ColliderやSphere ColliderなどのColliderコンポーネントを追加します。これがプレイヤーの手がオブジェクトに触れたことを判定するために使われます。 -
Rigidbodyの追加: オブジェクトにRigidbodyコンポーネントを追加します。これにより、オブジェクトが物理法則(重力など)に従うようになり、投げることが可能になります。掴むだけなら物理演算が不要な場合は、Is Kinematicにチェックを入れます。 -
VRCPickupの追加: オブジェクトにVRCPickupコンポーネントを追加します。これがオブジェクトを「掴める」ようにするための最も重要なコンポーネントです。 -
Udon Behaviourの追加: オブジェクトにUdon Behaviourコンポーネントを追加し、後述するスクリプトを割 り当てます。
VRCPickupの主要な設定項目
InteractionText: オブジェクトに照準を合わせたときに表示されるテキスト(例: 「掴む」)。UseText: オブジェクトを掴んでいるときに表示されるテキスト(例: 「使う」)。PickupOrientation: 掴んだときにオブジェクトが手のどの位置・向きになるかを設定します。AutoHoldのままか、詳細に設定したい場合はUseCustomを選択します。ExactGun/ExactGrip:UseCustom時に、掴む位置を微調整するためのTransformを割り当てます。
ピックアップ関連のUdonSharpイベント
VRCPickupがアタッチされたオブジェクトでは、プレイヤーの掴む・離す・使うといったアクションに応じて、以下のUdonSharpイベントが自動的に呼び出されます。
OnPickup(): オブジェクトがプレイヤーに掴まれた瞬間に呼び出されます。OnDrop(): オブジェクトがプレイヤーから離された瞬間に呼び出されます。OnPickupUseDown(): オブジェクトを掴んでいる状態で、Useボタン(VRのトリガーなど)が押された瞬間に呼び出されます。OnPickupUseUp(): オブジェクトを掴んでいる状態で、Useボタンが離された瞬間に呼び出されます。
重要なポイント: プレイヤーがオブジェクトをPickupすると、そのオブジェクトの所有権 (Ownership) は自動的にそのプレイヤーに移ります。これにより、掴んでいるプレイヤーがオブジェクトの状態を権威的に変更できるようになります。
実装例1: 掴むと光る魔法のクリスタル
プレイヤーが手に取ると光り、離すと光が消える、シンプルなピックアップオブジェクトです。
スクリプト: GlowingCrystal.cs
using UdonSharp;
using UnityEngine;
public class GlowingCrystal : UdonSharpBehaviour
{
[Tooltip("制御するライトコンポーネント")]
public Light crystalLight;
[Tooltip("制御するメッシュレンダラーのマテリアル発光")]
public MeshRenderer crystalRenderer;
private MaterialPropertyBlock propBlock;
void Start()
{
propBlock = new MaterialPropertyBlock();
// 初期状態では光を消しておく
SetGlow(false);
}
// 掴まれた時に呼び出される
public override void OnPickup()
{
SetGlow(true);
}
// 離された時に呼び出される
public override void OnDrop()
{
SetGlow(false);
}
private void SetGlow(bool isGlowing)
{
if (crystalLight != null)
{
crystalLight.enabled = isGlowing;
}
if (crystalRenderer != null)
{
crystalRenderer.GetPropertyBlock(propBlock);
if (isGlowing)
{
// Emissionを有効にし、色を白に設定
propBlock.SetFloat("_EmissionColor", 1.0f);
}
else
{
propBlock.SetFloat("_EmissionColor", 0.0f);
}
crystalRenderer.SetPropertyBlock(propBlock);
}
}
}
注意:
_EmissionColorなどのシェーダープロパティ名は、使用するシェーダーによって異なる場合があります。Standardシェーダーでは_EmissionColorはColor型として扱われるため、SetColorを使用する方が適切な場合があります。使用するシェーダーのプロパティを確認してください。
Unityでの設定
- クリスタルとなるオブジェクトに、子オブジェクトとして
Point Lightを追加します。初期状態ではenabledのチェックを外しておきます。 - クリスタルのマテリアルのシェーダーを、
StandardなどEmission(発光)をサポートするものに設定します。 GlowingCrystal.csをオブジェクトのUdon Behaviourに割り当て、Crystal Lightに子オブジェクトのライトを、Crystal Rendererに自身のMeshRendererを割り当てます。
これで、掴んだプレイヤーの視点ではクリスタルが光って見えます。しかし、このままでは他のプレイヤーには光って見えません。状態を同期させる必要があります。
実装例2: 状態が同期する懐中電灯
誰かが手に取ってスイッチを入れると、その懐中電灯が光っている状態が全プレイヤーで同期されるギミックです。
スクリプト: SyncedFlashlight.cs
using UdonSharp;
using UnityEngine;
using VRC.SDKBase;
public class SyncedFlashlight : UdonSharpBehaviour
{
[Tooltip("懐中電灯のライト部分")]
public Light flashlight;
[UdonSynced]
private bool isLightOn = false;
void Start()
{
// 初期状態を反映
UpdateLightState();
}
// 掴んでいる状態でUseボタンが押された時
public override void OnPickupUseDown()
{
// 所有権はPickup時に自動で移っているので不要
isLightOn = !isLightOn;
RequestSerialization(); // 状態の変更を通知
UpdateLightState();
}
public override void OnDeserialization()
{
// 他のプレイヤーの変更を受信して状態を反映
UpdateLightState();
}
private void UpdateLightState()
{
if (flashlight != null)
{
flashlight.enabled = isLightOn;
}
}
}
Unityでの設定
- 懐中電灯のモデルに
VRCPickup,Rigidbody,Collider,Udon Behaviourを追加します。 - 先端部分に
Spot Lightコンポーネントを追加し、Flashlightとして設定します。 SyncedFlashlight.csをUdon Behaviourに割り当て、FlashlightフィールドにSpot Lightをドラッグ&ドロップします。
これで、誰かが懐中電灯を掴んでUseボタン(トリガー)を押すと、ライトが点灯/消灯し、その状態がインスタンス内の全プレイヤーに同期されます。
まとめ
- オブジェクトを掴めるようにするには、
VRCPickup,Rigidbody,Colliderの3つのコンポーネントが基本セットです。 OnPickup,OnDrop,OnPickupUseDownなどのイベントを利用して、プレイヤーのアクションに応じた処理を実装します。- オブジェクトを
Pickupすると、所有権は自動的にそのプレイヤーに移ります 。これは同期ギミックを実装する上で非常に重要な仕様です。 - アイテムの状態(ON/OFFなど)を同期させるには、
[UdonSynced]変数とRequestSerialization()、そしてOnDeserialization()を組み合わせる、これまでのギミックと同様の同期パターンを使用します。
ピックアップオブジェクトは、プレイヤーに「ワールドに触れている」という感覚を与えるた めの強力な手段です。この基本を応用して、プレイヤーが思わず手に取りたくなるような、楽しいアイテムをたくさん作ってみましょう。