【VRChat】配列とデータ構造:List/Dictionaryなしで設計するテクニック

作成: 2025-12-19

複数データを効率的に管理する配列の使い方。ジェネリクス非対応環境でのList・Dictionary代替実装、[UdonSynced]配列の注意点。

概要

ワールド制作が高度になるにつれて、扱うデータの数も増えていきます。例えば、ゲームに参加している全プレイヤーのスコア、複数のスポーン地点の座標、パズルの各ピースの状態など、これらを一つ一つの独立した変数として管理するのは非効率的で、拡張性もありません。

このような場面で強力なツールとなるのが配列 (Array) です。配列を使うことで、同じ種類の複数のデータを一つの変数にまとめて、効率的に管理・操作することができます。

本記事では、UdonSharpにおける配列の基本的な使い方から、List<T>Dictionary<T, K>といったC#の標準的なデータ構造が使えないという制約の中で、どのようにして複雑なデータを管理していくかについて解説します。

1. 配列の基本

配列は、同じデータ型の要素が連番のインデックス(添え字)で並んだものです。インデックスは0から始まります。

配列の宣言

データ型[] 配列名; のように、データ型の後に[]を付けて宣言します。

// int型の配列を宣言
int[] scores;

// Transform型の配列を宣言
public Transform[] spawnPoints;

// UdonSharpBehaviour型の配列も可能
public PlayerCard[] playerCards;

配列の初期化と値へのアクセス

配列を使用するには、宣言後に要素数を指定して初期化する必要があります。

void Start()
{
    // 5つの要素を持つint型の配列を作成
    scores = new int[5];

    // インデックスを使って各要素に値を代入
    scores[0] = 100;
    scores[1] = 85;
    scores[2] = 92;
    scores[3] = 78;
    scores[4] = 88;

    // インデックスを使って値を取得
    Debug.Log($"2番目のプレイヤーのスコア: {scores[1]}"); // 出力: 85

    // publicな配列はInspectorでサイズと内容を設定できる
    // spawnPointsの最初の要素の位置にテレポート
    Networking.LocalPlayer.TeleportTo(spawnPoints[0].position, spawnPoints[0].rotation);
}

forループとの組み合わせ

配列の真価は、forループと組み合わせることで発揮されます。配列のLengthプロパティで要素数を取得し、全要素に対して同じ処理を繰り返し実行できます。

// 全プレイヤーのスコアを合計する
int totalScore = 0;
for (int i = 0; i < scores.Length; i++)
{
    totalScore += scores[i];
}
Debug.Log($"合計スコア: {totalScore}");

// --- 遅延イベントを使った巡回の例 ---
// 注意: SendCustomEventDelayedSecondsは引数を渡せないため、
// 以下のようにインデックスをフィールドで管理します

private int currentPointIndex = 0;

public void StartTour()
{
    currentPointIndex = 0;
    MoveToNextPoint();
}

public void MoveToNextPoint()
{
    if (currentPointIndex < spawnPoints.Length)
    {
        // 現在のポイントへ移動
        Debug.Log($"ポイント {currentPointIndex} へ移動");
        currentPointIndex++;

        // まだ次のポイントがあれば、5秒後に次へ
        if (currentPointIndex < spawnPoints.Length)
        {
            SendCustomEventDelayedSeconds(nameof(MoveToNextPoint), 5.0f);
        }
    }
}

2. 配列の同期

配列も[UdonSynced]属性を付けることで、ネットワーク同期の対象にすることができます。これにより、インスタンス内の全プレイヤーで同じ配列データを共有できます。

[UdonSynced]
private int[] playerScores = new int[8];

public void UpdateScore(int playerId, int newScore)
{
    // playerIdをインデックスとしてスコアを更新
    if (playerId >= 0 && playerId < playerScores.Length)
    {
        Networking.SetOwner(Networking.LocalPlayer, this.gameObject);
        playerScores[playerId] = newScore;
        RequestSerialization();
    }
}

public override void OnDeserialization()
{
    // 同期されたスコア配列を使ってUIなどを更新
    UpdateScoreboard();
}

注意: 配列全体を同期するのは、データ量が多くなる可能性があります。特に要素数が大きい配列や、内容が頻繁に変わる配列を同期させる場合は、ネットワーク負荷に注意が必要です。

3. UdonSharpにおける高度なデータ構造

C#では通常、サイズの可変なList<T>や、キーと値のペアを格納するDictionary<T, K>といった便利なデータ構造が多用されます。しかし、UdonSharpではジェネリクスがサポートされていないため、これらを直接使用することはできません。しかし、配列を工夫することで、その機能の一部を再現することは可能です。

List<T>の代替: 配列とカウンタ

リストのように、後から要素を追加していくような機能は、大きな配列をあらかじめ確保しておき、現在いくつの要素が使われているかを別のint変数で管理することで擬似的に実装できます。

// 最大100人まで名前を記録できるリストの代替
private string[] nameList = new string[100];
private int nameCount = 0;

public void AddName(string newName)
{
    // 配列に空きがある場合のみ追加
    if (nameCount < nameList.Length)
    {
        nameList[nameCount] = newName;
        nameCount++;
    }
}

Dictionary<T, K>の代替: 2本の配列

辞書(連想配列)のように、特定のキー(例: プレイヤー名)に対応する値(例: スコア)を管理したい場合は、キー用の配列と値用の配列を2本用意し、同じインデックスで関連付けます。

// プレイヤー名とスコアを関連付ける辞書の代替
private string[] playerNames = new string[8];
private int[] playerScores = new int[8];

// プレイヤー名からスコアを取得する
public int GetScoreByName(string targetName)
{
    for (int i = 0; i < playerNames.Length; i++)
    {
        if (playerNames[i] == targetName)
        {
            return playerScores[i]; // 対応するインデックスのスコアを返す
        }
    }
    return -1; // 見つからなかった場合
}

これは効率的な方法ではありませんが、UdonSharpの制約下で辞書的な機能を実現するための一つのアプローチです。

まとめ

  • 配列は、同じ型の複数のデータをまとめて扱うための基本的なデータ構造です。
  • 配列はforループと組み合わせることで、全要素に対する反復処理を効率的に記述できます。
  • 配列に[UdonSynced]を付けることで、内容をネットワーク同期できますが、データ量には注意が必要です。
  • UdonSharpではList<T>Dictionary<T, K>が使えないため、配列と他の変数を組み合わせることで、その機能を擬似的に実装する必要があります。
  • 配列のサイズは一度作成すると変更できないため、必要な要素数をあらかじめ想定して大きめに確保しておく、といった設計上の工夫が求められます。

配列を使いこなすことは、プレイヤー管理、アイテム管理、ステージ管理など、複雑なシステムを構築する上で避けては通れない道です。UdonSharpの制約を理解し、配列を駆使してデータを巧みに操る方法をマスターしましょう。