【VRChat】ピックアップオブジェクト入門:掴める・使えるアイテムを作る

作成: 2025-12-19

プレイヤーが手に取って操作できるアイテムの実装。VRCPickupコンポーネント、OnPickup/OnDrop/OnPickupUseDownイベント、状態の同期方法。

概要

ワールド内にただ置かれているだけでなく、プレイヤーが実際に手に取って、振ったり、使ったり、投げたりできるピックアップオブジェクトは、ワールドとのインタラクションを格段に深める重要な要素です。懐中電灯、ボール、武器、食べ物など、その応用範囲は無限大です。

UdonSharpでピックアップオブジェクトを実装するには、VRChat SDKが提供するVRCPickupコンポーネントと、それに関連するUdonSharpのイベントを利用します。

本記事では、基本的なピックアップオブジェクトの設定方法から、掴んだり離したりした際のイベント処理、そしてオブジェクトの状態を同期させる方法までを解説します。

ステップ1: VRCPickupコンポーネントの設定

まず、プレイヤーが掴めるようにしたいオブジェクトに、必要なコンポーネントを追加します。

  1. オブジェクトの準備: シーン内に、プレイヤーに掴ませたい3Dモデル(例: Sphere、Cube)を配置します。

  2. Colliderの追加: オブジェクトにBox ColliderSphere ColliderなどのColliderコンポーネントを追加します。これがプレイヤーの手がオブジェクトに触れたことを判定するために使われます。

  3. Rigidbodyの追加: オブジェクトにRigidbodyコンポーネントを追加します。これにより、オブジェクトが物理法則(重力など)に従うようになり、投げることが可能になります。掴むだけなら物理演算が不要な場合は、Is Kinematicにチェックを入れます。

  4. VRCPickupの追加: オブジェクトにVRCPickupコンポーネントを追加します。これがオブジェクトを「掴める」ようにするための最も重要なコンポーネントです。

  5. 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シェーダーでは_EmissionColorColor型として扱われるため、SetColorを使用する方が適切な場合があります。使用するシェーダーのプロパティを確認してください。

Unityでの設定

  1. クリスタルとなるオブジェクトに、子オブジェクトとしてPoint Lightを追加します。初期状態ではenabledのチェックを外しておきます。
  2. クリスタルのマテリアルのシェーダーを、StandardなどEmission(発光)をサポートするものに設定します。
  3. 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での設定

  1. 懐中電灯のモデルにVRCPickup, Rigidbody, Collider, Udon Behaviourを追加します。
  2. 先端部分にSpot Lightコンポーネントを追加し、Flashlightとして設定します。
  3. SyncedFlashlight.csをUdon Behaviourに割り当て、FlashlightフィールドにSpot Lightをドラッグ&ドロップします。

これで、誰かが懐中電灯を掴んでUseボタン(トリガー)を押すと、ライトが点灯/消灯し、その状態がインスタンス内の全プレイヤーに同期されます。

まとめ

  • オブジェクトを掴めるようにするには、VRCPickup, Rigidbody, Colliderの3つのコンポーネントが基本セットです。
  • OnPickup, OnDrop, OnPickupUseDownなどのイベントを利用して、プレイヤーのアクションに応じた処理を実装します。
  • オブジェクトを Pickupすると、所有権は自動的にそのプレイヤーに移ります 。これは同期ギミックを実装する上で非常に重要な仕様です。
  • アイテムの状態(ON/OFFなど)を同期させるには、[UdonSynced]変数とRequestSerialization()、そしてOnDeserialization()を組み合わせる、これまでのギミックと同様の同期パターンを使用します。

ピックアップオブジェクトは、プレイヤーに「ワールドに触れている」という感覚を与えるための強力な手段です。この基本を応用して、プレイヤーが思わず手に取りたくなるような、楽しいアイテムをたくさん作ってみましょう。