概要
UdonSharpはC#の文法を用いてプログラミングを行いますが、そのコードはUdon VMという特殊な環境で動作するため、標準的なC#の全ての機能が利用できるわけではありません。開発を効率的に進めるためには、何が利用でき、何が利用できないのかを正確に把握しておくことが不可欠です。
本記事では、UdonSharp開発において使用可能なC#の主要機能と、Udon VMの制約によって課される主な制限事項について具体的に解説します。
利用可能なC#の主要機能
UdonSharpでは、C#の基本的な機能の多くがサポートされています。これらを組み合わせることで、ほとんどのギミックを実装することが可能です。
1. 変数とデータ型
- 基本データ型:
int,float,double,bool,string,charなどの基本的な値型はすべて利用できます。 - Unityの型:
Vector3,Quaternion,Color,GameObjectなど、Unityエンジンが提供するほとんどのクラスや構造体を利用できます。 - 配列:
int[],GameObject[]のような一次元配列は問題なく使用できます。多次元配列(例:int[,])もサポートされています。
public class VariableExample : UdonSharpBehaviour
{
// 基本データ型
public int score = 0;
public string playerName = "Default Player";
public bool isGameActive = false;
// Unityの型
public Vector3 initialPosition = new Vector3(0, 1, 0);
public GameObject targetObject;
// 配列
public Transform[] waypoints;
}
2. 制御構文
プログラムの流れを制御するための構文は、標準的なC#と同様に使用できます。
- 条件分岐:
if,else if,else,switch - 繰り返し:
for,foreach,while,do-while - ジャンプ:
break,continue,return
void CheckScore()
{
if (score >= 100)
{
Debug.Log("勝利!");
}
else
{
Debug.Log("まだ足りません");
}
}
void ProcessWaypoints()
{
foreach (Transform waypoint in waypoints)
{
Debug.Log(waypoint.name);
}
}
3. メソッド(関数)
処理をまとめるためのメソッドも定義・呼び出しが可能です。引数や戻り値もサポートされています。
// 戻り値と引数を持つメソッド
private int Add(int a, int b)
{
return a + b;
}
void CalculateSum()
{
int result = Add(5, 3); // resultは8になる
Debug.Log(result);
}
4. プロパティ
C#のプロパティ(getterとsetter)もサポートされています。
private int _health = 100;
public int Health
{
get { return _health; }
set { _health = Mathf.Clamp(value, 0, 100); }
}
5. その他のサポートされている機能
以下の機能もUdonSharpで利用可能です。
- out/refパラメータ: メソッドの引数で
outやrefキーワードが使用できます。 - 拡張メソッド: 既存の型に独自のメソッドを追加できます。
- null条件演算子:
?.や??演算子が使用できます。 - 文字列補間:
$"スコア: {score}"のような書き方ができます。
// out パラメータの例
public bool TryGetPlayerScore(string playerName, out int score)
{
// 処理...
score = 100;
return true;
}
// 呼び出し側
if (TryGetPlayerScore("Player1", out int result))
{
Debug.Log($"スコア: {result}");
}
6. クラスと継承
UdonSharpBehaviourを継承するクラスを作成するのが基本です。
重要な制限: UdonSharpでは、独自のカスタムクラスを
newキーワードでインスタンス化することはサポートされていません。データを管理したい場合は、複数の変数を直接定義するか、配列を使用します。
// UdonSharpでの正しいデータ管理方法
public class GameManager : UdonSharpBehaviour
{
// 複数の変数を直接定義する
private int playerHealth;
private int playerMana;
private string playerName;
// 複数プレイヤーのデータを管理する場合は配列を使用
private int[] playerScores;
void Start()
{
playerHealth = 100;
playerMana = 50;
playerName = "Player";
playerScores = new int[10]; // 配列の初期化は可能
}
}
主な制限事項と代替策
Udon VMの設計思想(セキュリ ティとクロスプラットフォーム互換性)により、いくつかのC#機能は意図的に制限されています。
1. ジェネリクス (Generics)
- 制限:
List<T>,Dictionary<T, K>,Queue<T>のようなジェネリックコレクションは使用できません。 - 理由: Udon VMが実行時に型を動的に生成するジェネリクスをサポートしていないためです。
- 代替策:
- 配列 (
T[]): 最も基本的な代替策。要素数が固定または最大数が予測できる場合に使用します。 - VRChat SDKのDataToken:
DataListやDataDictionaryといった、Udonで利用可能なデータコンテナを使用します。これらはネットワーク経由での同期も考慮されています。(詳細は高度なトピック編で解説します)
- 配列 (
2. ファイルI/O (System.IO)
- 制限:
System.IO名前空間に含まれるクラス(File,StreamReaderなど)は使用できません。ローカルファイルへの読み書きは一切行えません。 - 理由: セキュリティのため。ワールドが悪意のあるプログラムによってユーザーのPC上のファイルを読み書きするのを防ぎます。
- 代替策: ワールドにデータを保存したい場合は、
VRChat SDKが提供するVRCPlayerApiの永続化機能などを検討する必要があります(上級者向け)。
3. ネットワーク通信 (System.Net)
- 制限:
HttpClientやWebRequestなど、任意のWebサーバーと通信するための機能は使用できません。 - 理由: セキュリティのため。フィッシングサイトへの誘導や、外部サーバーへの情報送信を防ぎます。
- 代替策:
VRChat SDKが提供するVRCStringDownloaderなど、VRChatが許可した一部の機能のみ利用可能です。
4. ポインタとアンセーフコード
- 制限:
unsafeキーワードやポインタ演算は使用できません。 - 理由: メモリを直接操作するポインタは、プログラムを不安定にしたり、セキュリティホールを生み出したりする危険性が高いためです。
5. インターフェース
- 制限: C#のインターフェース(
interface)は使用できません。 - 理由: Udon VMがインターフェースの概念をサポートしていないためです。
- 代替策: 複数のスクリプト間で共通の処理を定義したい場合は、基底クラスからの継承や、
SendCustomEventを使った疎結合な設計を検討します。
6. メソッドオーバーロード
- 制限: 同じ名前で引数の型や数が異なるメソッド(オーバーロード)は使用できません。
- 理由: Udon VMが同名メソッドの区別をサポートしていないためです。
- 代替策: 異なる名前のメソッドを定義します。例えば、
Move(Vector3 target)とMoveToPlayer(VRCPlayerApi player)のように、名前で用途を区別します。
// ❌ 動作しない例(オーバーロード)
// public void Move(Vector3 target) { }
// public void Move(Transform target) { }
// ✅ 正しい代替案
public void MoveToPosition(Vector3 target) { }
public void MoveToTransform(Transform target) { }
7. 例外処理(try/catch)
- 制限:
try-catch-finally構文による例外処理は使用できません。 - 代替策: エラーが発生しそうな処理の前に、事前に条件をチェックする防御的なコーディングを行います。
// ❌ 使用不可
// try { DoSomething(); } catch { }
// ✅ 代替: 事前チェック
if (targetObject != null)
{
DoSomething();
}
制限事項のまとめ
| 機能分類 | 制限内容 | 代替策 |
|---|---|---|
| ジェネリクス | List<T>, Dictionary<T, K> など | 配列 (T[]), DataList |
| インターフェース | interfaceの定義・実装 | 継承またはSendCustomEvent |
| メソッドオーバーロード | 同名で引数違いのメソッド | 異なる名前のメソッドを定義 |
| 例外処理 | try-catch-finally構文 | 事前の条件チェック(防御的コーディング) |
| ファイルI/O | System.IO を使ったファイル読み書き | なし(セ キュリティ上の制約) |
| ネットワーク | System.Net を使った任意通信 | VRCStringDownloader などSDK提供機能 |
| ポインタ | unsafe コード、ポインタ演算 | なし(セキュリティ上の制約) |
| スレッディング | System.Threading を使ったマルチスレッド | なし(Udonはシングルスレッドで動作) |
サポート状況の早見表
| 機能 | サポート状況 |
|---|---|
基本データ型(int, float, bool, string) | ✅ 対応 |
配列(T[]) | ✅ 対応 |
制御構文(if, for, switchなど) | ✅ 対応 |
| メソッド定義 | ✅ 対応 |
| プロパティ(getter/setter) | ✅ 対応 |
out/refパラメータ | ✅ 対応 |
| 拡張メソッド | ✅ 対応 |
null条件演算子(?., ??) | ✅ 対応 |
ジェネリクス(List<T>など) | ❌ 非対応 |
| インターフェース | ❌ 非対応 |
| メソッドオーバーロード | ❌ 非対応 |
例外処理(try-catch) | ❌ 非対応 |
async/await | ❌ 非対応 |
制限に直面した際の考え方
UdonSharpで開発していると、普段のC#開発で当たり前に使っていた機能が使えずに戸惑うことがあります。その際は、以下の点を意識することが重要です。
- エラーメッセージを読む: UdonSharpのコンパイラは、サポートされていない機能を使おうとすると、多くの場合「
[UdonSharp] The C# feature 'X' is not supported」といった明確なエラーメッセージを出力します。まずはコンソールを確認しましょう。 - 代替手段を探す: なぜその機能を使いたかったのか、という目的を考え、配列やUdonSharpが提供する別の機能で同じことが実現できないか検討します。
- 公式ドキュメントを参照する: VRChatやUdonSharpの公式ドキュメントには、特定の目的を達成するための推奨される方法が記載されていることがあります。
まとめ
- UdonSharpでは、変数、制御構文、メソッドといったC#の基本的な機能の多くが利用可能です。
- 一方で、ジェネリクス、ファイルI/O、自由なネットワーク通信など、Udon VMの制約により利用できない機能も存在します。
- これらの制限は、主にセキュリティとクロスプラットフォーム互換性を確保するために設けられています。
- 制限に直面した場合は、エラーメッセージを確認し、配列などの代替手段で目的を達成する方法を検討することが重要です。
利用できる機能とできない機能の境界線を理解することは、UdonSharpを効率的に使いこなすための第一歩です。