Unity は、サポートしているすべてのプラットフォームで共通のスクリプティング API と体験を提供します。ただし、独自の制限があるプラットフォームもあります。これらの制限を理解するために、以下の表では、各プラットフォームとスクリプティングバックエンドに適用される制限について説明します。
| プラットフォーム (スクリプティングバックエンド) | 事前 (AOT) コンパイル | スレッドをサポート |
|---|---|---|
| Android (IL2CPP) | はい | はい |
| Android (Mono) | いいえ | はい |
| iOS (IL2CPP) | はい | はい |
| スタンドアロン (IL2CPP) | はい | はい |
| スタンドアロン (Mono) | いいえ | はい |
| ユニバーサル Windows プラットフォーム (IL2CPP) | はい | はい |
| ウェブ (IL2CPP) | はい | いいえ |
一部のプラットフォームでは、ランタイムにコードを生成できません。ターゲットデバイスでの ジャストインタイム (JIT) コンパイルに依存するマネージコードは失敗します。代わりに、すべてのマネージコードを 事前 (AOT) にコンパイルする必要があります。多くの場合、この区別は問題になりませんが、いくつかの特定のケースでは、AOT プラットフォームでその他にも考慮事項があります。
Unity は AOT プラットフォームでのリフレクションをサポートします。ただし、このコンパイラーがコードをリフレクション経由で使用していると推測できない場合、コードはランタイムに存在しない可能性があります。詳細については、マネージコードのストリッピングを参照してください。
AOT プラットフォームは、System.Reflection.Emit 名前空間のメソッドを実装できません。
事前 (AOT) コンパイルが必要なプラットフォームでは、リフレクションの使用が原因でシリアル化と逆シリアル化で問題が発生することがあります。シリアル化と逆シリアル化の一部として、型とメソッドをリフレクション経由でのみ使用できる場合、事前コンパイラーは型とメソッドのためにコードが生成される必要があることを検知することができません。
汎用型とメソッドの場合、汎用インスタンスによって必要なコードが異なる場合があるため、コンパイラーはどの汎用インスタンスを使用するかを決定する必要があります。例えば、List<int> のコードは List<double> のコードとは異なります。ただし、IL2CPP は参照型の使用コードを共有するため、List<object> と List<string> に同じコードが使用されます。
以下の場合、IL2CPP がコンパイル時間を検出できなかった汎用型およびメソッド参照できます。
Activator.CreateInstance(typeof(SomeGenericType<>).MakeGenericType(someType));
typeof(SomeGenericType<>).MakeGenericType(someType)).GetMethod(“AMethod”).Invoke(null, null);
typeof(SomeType).GetMethod(“GenericMethod”).MakeGenericMethod(someType).Invoke(null, null);
Struct<Struct<Struct<...<Struct<int>>>> など、深くネストされた汎用値型の呼び出し。これらのケースをサポートするために、IL2CPP は任意の型パラメーターで動作する汎用コードを生成します。ただし、このコードは型のサイズやリファレンス型か値型であるかを仮定できないため、処理が遅くなります。より高速な汎用メソッドを生成する必要がある場合は、以下のことを行います。
where: class 制約を追加します。その後、IL2CPP はリファレンス型共有を使用してフォールバックメソッドを生成し、パフォーマンスの低下を起こさないようにします。where: struct 制約を追加します。これにより、いくつかの最適化が可能になりますが、値の型が異なるサイズになる可能性があるため、コードの実行は引き続き低速になります。UsedOnlyForAOTCodeGeneration という名前のメソッドを作成し、IL2CPP で生成する汎用とメソッドへの参照を追加します。このメソッドを呼び出す必要はありません (おそらく呼び出すべきではありません)。以下の例では、GenericType<MyStruct> の特殊化が確実に生成されます。public void UsedOnlyForAOTCodeGeneration()
{
// Ensure that IL2CPP will create code for MyGenericStruct
// using MyStruct as an argument.
new GenericType<MyStruct>();
// Ensure that IL2CPP will create code for SomeType.GenericMethod
// using MyStruct as an argument.
new SomeType().GenericMethod<MyStruct>();
public void OnMessage<T>(T value)
{
Debug.LogFormat("Message value: {0}", value);
}
// Include an exception so we can be sure to know if this
// method is ever called.
throw new InvalidOperationException(
"This method is used for AOT code generation only. " +
"Do not call it at runtime.");
}
“Faster (smaller) builds” 設定を有効にすると、完全に共有可能な汎用コードのバージョンが 1 つのみコンパイルされます。これにより、生成されるメソッドの数が減り、コンパイル時間とビルドサイズが削減されますが、ランタイムパフォーマンスが犠牲になります。
ネイティブコードから呼び出せるように C 言語の関数ポインターにマーシャリングを行う必要があるマネージメソッドには、AOT プラットフォーム上でいくつかの制限があります。
[MonoPInvokeCallback] 属性が必要です。[MonoPInvokeCallback(Type)] オーバーロードを使用して、サポートする必要がある汎用特殊化を指定する必要があります。その場合、その型は正しい数の汎用引数を持つジェネリックインスタンスである必要があります。以下のように、メソッドに複数の [MonoPInvokeCallback] 属性を設定することができます。// Generates reverse P/Invoke wrappers for NameOf<long> and NameOf<int>
// Note that the types are only used to indicate the generic arguments.
[MonoPInvokeCallback(typeof(Action<long>))]
[MonoPInvokeCallback(typeof(Action<int>))]
private static string NameOfT<T>(T item)
{
return typeof(T).Name;
}
一部のプラットフォームではスレッドの使用がサポートされないため、System.Threading 名前空間を使用するマネージコードはランタイムに失敗します。また、.NET クラスの一部は暗示的にスレッドに依存します。頻繁に使用される例は、スレッドのサポートに依存する System.Timers.Timer クラスです。
IL2CPP は例外フィルターをサポートしますが、IL2CPP は C++ の例外を使用してマネージ例外を実装するため、実行順フィルター文と catch ブロックは異なります。フィルターがフィールドへの書き込みをブロックしない限り、これに気づくことはないでしょう。
IL2CPP は、MarshalAs 属性と FieldOffset 属性をランタイムに反映することをサポートしていません。IL2CPP はコンパイル時にはこれらの属性をサポートしています。これらは、適切なプラットフォーム呼び出しマーシャリングに使用してください。
IL2CPP は、C# の dynamic キーワードをサポートしていません。このキーワードには JIT コンパイルが必要ですが、IL2CPP では不可能です。
IL2CPP は Marshal.Prelink または Marshal.PrelinkAll API メソッドをサポートしません。
IL2CPP は System.Diagnostics.Process API メソッドをサポートしません。デスクトッププラットフォームでこれが必要な場合は、Mono スクリプティングバックエンドを使用します。