Unity는 지원하는 모든 플랫폼에 동일한 스크립팅 API와 경험을 제공하고자 노력하고 있습니다. 하지만 몇몇 플랫폼에는 각각 고유한 제약이 있습니다. 이러한 제약을 이해하도록 돕고 크로스 플랫폼 코드를 지원하기 위해, 각각의 제약이 어떤 플랫폼과 스크립팅 백엔드에 적용되는지가 아래 표에 설명되어 있습니다.
플랫폼(스크립팅 백엔드) | AOT(ahead-of-time) 컴파일 | 스레드 없음 | .NET 코어 클래스 라이브러리 서브셋 | |
---|---|---|---|---|
Android(IL2CPP) | ✔ | |||
Android(Mono) | ||||
iOS(IL2CPP) | ✔ | |||
PlayStation 4(IL2CPP) | ✔ | |||
PlayStation Vita(IL2CPP) | ✔ | |||
Standalone(IL2CPP) | ✔ | |||
Standalone(Mono) | ||||
Switch(IL2CPP) | ✔ | |||
유니버설 Windows 플랫폼(IL2CPP) | ✔ | |||
유니버설 Windows 플랫폼(.NET) | ✔ | |||
WebGL(IL2CPP) | ✔ | ✔ | ||
WiiU(Mono) | ||||
XBox One(IL2CPP) | ✔ |
몇몇 플랫폼은 런타임 코드 생성을 지원하지 않습니다. 따라서 타겟 디바이스에서 JIT(just-in-time) 컴파일에 의존하는 일부 관리되는 코드는 작동하지 않습니다. 이 경우 관리되는 코드를 AOT(ahead-of-time) 방식으로 컴파일해야 합니다. 많은 경우 이러한 방식의 차이는 없지만, 몇몇 경우에서는 AOT 플랫폼을 사용할 때 몇 가지 고려해야 할 사항이 있습니다.
AOT 플랫폼은 System.Reflection.Emit
네임스페이스의 메서드를 구현할 수 없습니다. 다만, 컴파일러가 리플렉션을 통해 사용된 코드가 런타임 시 존재해야 한다고 추측할 수 있는 경우에 한해 System.Reflection
의 나머지 부분을 사용할 수 있습니다.
AOT 플랫폼에서는 리플렉션을 사용할 경우 직렬과와 역직렬화에서 문제가 발생할 수 있는데, 타입 또는 메소드가 순수히 리플렉션을 통해 직렬화와 역직렬화의 일부로 사용될 경우 AOT 컴파일러가 타입이나 메소드에 대한 코드를 생성해야 할 지 알 수 없기 때문입니다.
일반 메서드에서는 디바이스에서 실제로 실행되기 위해 개발자가 작성한 코드를 컴파일러가 추가 작업을 통해 확장시켜야 합니다. 예를 들어 int
나 double
이 있는 경우 List
에 대한 다른 코드를 필요로 합니다. 동작이 컴파일 도중이 아닌 런타임 시점에 결정되는 가상 메서드가 존재하는 경우, 컴파일러는 소스 코드에서는 명백하지 않더라도 런타임 코드 생성을 쉽게 요구할 수 있게 됩니다.
다음 예제 코드는 JIT 플랫폼에서는 의도한 대로 작동하여, 콘솔에 “Message value: Zero”를 한 번 출력합니다.
using UnityEngine;
using System;
public class AOTProblemExample : MonoBehaviour, IReceiver
{
public enum AnyEnum
{
Zero,
One,
}
void Start()
{
// Subtle trigger: The type of manager *must* be
// IManager, not Manager, to trigger the AOT problem.
IManager manager = new Manager();
manager.SendMessage(this, AnyEnum.Zero);
}
public void OnMessage<T>(T value)
{
Debug.LogFormat("Message value: {0}", value);
}
}
public class Manager : IManager
{
public void SendMessage<T>(IReceiver target, T value) {
target.OnMessage(value);
}
}
public interface IReceiver
{
void OnMessage<T>(T value);
}
public interface IManager
{
void SendMessage<T>(IReceiver target, T value);
}
하지만 IL2CPP 스크립팅 백엔드가 있는 AOT 플랫폼에서 이 코드를 실행하면 아래의 예외 오류가 발생합니다.
ExecutionEngineException: Attempting to call method 'AOTProblemExample::OnMessage<AOTProblemExample+AnyEnum>' for which no ahead of time (AOT) code was generated.
at Manager.SendMessage[T] (IReceiver target, .T value) [0x00000] in <filename unknown>:0
at AOTProblemExample.Start () [0x00000] in <filename unknown>:0
유사하게도, Mono 스크립팅 백엔드에서는 아래의 예외 오류가 발생합니다.
ExecutionEngineException: Attempting to JIT compile method 'Manager:SendMessage<AOTProblemExample/AnyEnum> (IReceiver,AOTProblemExample/AnyEnum)' while running with --aot-only.
at AOTProblemExample.Start () [0x00000] in <filename unknown>:0
AOT 컴파일러는 AnyEnum
의 T
와 일반 메서드 OnMessage
에 대한 코드를 생성해야 함을 인지하지 못합니다. 따라서 컴파일러는 이 메서드를 무시하게 됩니다. 메서드가 호출되어도 런타임이 호출할 올바른 코드를 찾지 못하므로 이 오류 메시지를 반환합니다.
이러한 AOT 문제를 해결하려면 강제로 컴파일러가 올바른 코드를 생성하도록 만들 수 있습니다. 예를 들어 AOTProblemExample
클래스에 아래와 같은 메서드를 추가할 수 있습니다.
public void UsedOnlyForAOTCodeGeneration()
{
// IL2CPP needs only this line.
OnMessage(AnyEnum.Zero);
// Mono also needs this line. Note that we are
// calling directly on the Manager, not the IManager interface.
new Manager().SendMessage(null, AnyEnum.Zero);
// 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.");
}
컴파일러가 AnyEnum
의 T
와 OnMessage
에 대한 명시적 호출을 받으면, 런타임이 실행할 올바른 코드를 생성합니다. UsedOnlyForAOTCodeGeneration
메서드는 호출되기 위한 목적이 아닌, 컴파일러가 감지하기 위한 목적입니다.
네이티브 코드에서 호출할 수 있도록 C 함수 포인터로 마셜링해야 하는 관리되는 메서드에는 AOT 플랫폼에 대한 몇 가지 제한 사항이 있습니다.
[MonoPInvokeCallback]
속성이 있어야 합니다.몇몇 플랫폼은 스레드를 지원하지 않으므로, System.Threading
네임스페이스를 사용하는 관리되는 코드는 런타임 도중 작동하지 않습니다. 또한, .NET 클래스 라이브러리의 일부분은 스레드에 암시적으로 의존합니다. 자주 사용되는 예제로는 System.Timers.Timer
클래스가 있는데, 이 클래스는 스레드 지원에 의존합니다.
IL2CPP는 C# 예외 필터를 지원하지 않습니다. 예외 필터를 사용하는 코드는 올바른 catch
블록으로 수정해야 합니다.
2019–05–28 페이지 수정됨
Samsung TV 지원 제외됨
Unity 2018.3에서 .Net 3.5 스크립팅 런타임이 지원 중단됨 NewIn20183