도메인 다시 로드 건너뛰기: MethodReload 기능을 사용한 더 빠른 반복

이 웹페이지는 이해를 돕기 위해 기계 번역으로 제공됩니다. 기계 번역으로 제공되는 콘텐츠에 대한 정확도나 신뢰도는 보장되지 않습니다. 번역된 콘텐츠의 정확도에 관해 의문이 있는 경우 웹페이지의 공식 영어 원문을 참고해 주시기 바랍니다.
우리 모두 그런 적이 있죠. 게임플레이 코드를 조금 수정하고 에디터가 도메인 리로드를 완료하기를 기다리는 것 말이에요. 그 과정은 반복 시간을 늦추고 창의력을 정말 고갈시킬 수 있습니다. 장치에서 반복하는 데 소요되는 추가 시간은 말할 것도 없으며, 각 사이클마다 전체 빌드가 필요합니다.
우리의 대답은 MethodReload입니다. Unite Seoul 무대에서 시연했으며 현재 실험적 패키지로 이용 가능합니다. 이 게시물에서는 MethodReload 사용 방법, 한계점, 그리고 프로젝트에 이미 가지고 있을 수 있는 핫 리로드 도구와 어떻게 비교되는지 다룰 것입니다.
우리가 계속 다시 로드되는 이유
여기서 약간의 역사가 도움이 되는데, 도메인 리로드가 존재하는 이유가 그것을 없애기 어려운 이유이기도 합니다.
플레이 모드는 Mono AppDomain을 기반으로 구축되었으며, 코드 변경 시 익숙한 프로세스를 시작했습니다. 즉, 도메인을 해체하고, 새 어셈블리를 로드하고, 재-JIT를 수행한 다음, 동일한 프로세스에서 재개하는 방식이었습니다. 수년 동안 그것은 진정한 초능력이었고, Unity 반복 작업하는 것이 플레이어를 구축하는 것보다 빠르다고 느껴졌던 큰 이유 중 하나입니다.
그것은 또한 둔기이기도 한데, 이는 재장전 중에 모든 것이 사라지고 하나를 바꾸든 백 개를 바꾸든 모든 것이 다시 돌아온다는 것을 의미합니다. 게다가 IL2CPP는 도메인 리로드를 전혀 지원하지 않으므로, 모든 변경 사항에 대해 전체 플레이어를 다시 빌드해야 합니다.
"CoreCLR가 이것을 해결하지 않을까요?"라고 스스로에게 물을 수도 있지만, 대답은 다음과 같습니다. 마법처럼이 아니라. CoreCLR이 도메인 리로드를 없애지만, 여전히 어셈블리를 다시 로드해야 하며 MethodReload도 여기서 도움을 줄 수 있습니다.
MethodReload 사용 방법
위에 언급했듯이, MethodReload는 현재 com.unity.pipeline에서 사용 가능하며, 현재 0.7.0-exp.1 버전입니다.
MethodReload를 사용하는 과정은 상당히 간단합니다. 하나 이상의 메서드에 [MethodReload] 속성을 지정하고, 워처를 시작하고, 플레이 모드로 들어가거나 개발 플레이어를 연결한 다음, 메서드의 본문을 편집하십시오. 실행 중인 인스턴스는 다음 호출에서 새 본체를 가져옵니다.
하지만 MethodReload는 도메인 리로드를 완전히 대체하지는 않습니다. 더 큰 변경 사항도 여전히 일반적인 컴파일을 거칩니다. 하지만 반복 루프의 대부분을 차지하는 작은 조정들에 있어서는 에디터와 기기 모두에서 긴 기다림을 몇 초의 문제로 바꿉니다.
사용해 보세요
MethodReload를 이해하는 가장 빠른 방법은 실행해보는 것이므로, 직접 테스트해보고 싶다면 시작하는 데 도움이 되는 몇 가지 단계를 알려드립니다. 참고: 이 기능은 실험적인 것이므로 향후 프로세스가 변경되거나 여기에 설명된 것과 다를 수 있습니다.
1. 패키지를 설치하세요 (Unity 6.0 이상 필요). Unity CLI를 통해 설치하거나 "기술적 이름으로 패키지 설치"를 사용하여 이 문자열( com.unity.pipeline@0.7.0-exp.1)로 패키지 관리자를 통해 Pipeline 패키지를 수동으로 추가할 수 있습니다.
2. 반복하는 메서드에 태그를 지정하세요. 디스패치 확인이 컴파일 시간에 짜여 있기 때문에, 플레이 모드로 들어가거나 플레이어를 빌드하기 전에 해당 속성이 있어야 합니다. 태그가 지정된 메서드는 public (인스턴스 또는 정적)이어야 하며 void 또는 IEnumerator; 다른 것은 빌드 시 경고와 함께 건너뜁니다.
using Unity.Pipeline.MethodReload;
using UnityEngine;
public class Spinner : MonoBehaviour
{
public float rotationSpeed = 90f;
[MethodReload]
public void Update()
{
transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime);
}
}3. 감시자를 시작하세요. MethodReload가 변경 사항을 실시간으로 볼 수 있도록 하려면 MethodReload Watcher를 시작해야 합니다. 프로젝트 설정 → 파이프라인 → 에디터로 이동한 다음, "메서드 인터프리터 감시 다시 로드" 섹션에서 시작 감시를 클릭 수.

감시자가 전체 에셋 트리를 덮는 동안, 저장된 .cs는 MethodReload 속성을 포함할 때만 컴파일되므로 관련 없는 저장 파일은 아무런 비용이 들지 않습니다.
4. 에디터에서 테스트하는 경우: 플레이 모드를 입력하세요. 실행되면, 태그가 지정된 모든 코드 변경 사항은 컴파일되어 제자리에 주입됩니다. 플레이 모드를 벗어나면 변경 사항이 컴파일되고 일반적인 도메인 리로드를 거칩니다.
5. 기기에서 테스트하는 경우: 기기를 연결하십시오. 런타임 파이프라인 드라이버가 활성화된 개발 플레이어를 구축하십시오. Autoconnect Profiler를 사용하여 장치를 연결하거나 Profiler 또는 콘솔 창 통해 연결하십시오. 그 후부터는 모든 저장 시 에디터에서 player's 전처리기 정의를 사용하여 컴파일하고 연결된 모든 플레이어에게 푸시합니다. 어느 방향으로도 재시작이 필요하지 않습니다.
미리 알아두면 좋을 두 가지 워크플로우 세부 사항:
- 워처가 실행되는 동안 자동 새로고침이 꺼져 있으므로 저장 시 도메인 새로고침 대신 다시 로드됩니다. 다른 에셋들은 Ctrl/Cmd+R을 누르기 전까지 가져와지지 않으며, 중지하기(Stop Watching)를 누르면 설정이 원래대로 돌아옵니다.
- 워치는 도메인 리로드에서 살아남습니다. 이는 플레이 모드 리로드로 지워진 편집 사항을 다시 적용하고, 재시작된 플레이어는 원래 컴파일된 코드를 부팅하기 때문에 재연결하는 플레이어에게 다시 푸시하기 때문입니다.
CLI를 사용하는 것은 어때요?
터미널에서 작업하는 것을 선호하거나, 에이전트를 통해 작업하거나, 감시가 시작된 이후 변경된 내용만 푸시하는 대신 태그가 지정된 모든 메서드를 푸시하고 싶다면, 동일한 기능이 Unity CLI 명령으로 노출됩니다.
# Reload in the Editor through the interpreter
unity command reload_file_editor_interpreter "Assets/Scripts/Spinner.cs"
# Compile in the Editor, push to connected players (folders work too)
unity command reload_file_player_interpreter "Assets/Scripts" --player 2한계
도메인 리로드 없이 타협하지 않는 방법은 없으며, MethodReload의 제한 사항은 세 가지가 있습니다. 적용할 수 있는 변경의 종류, 실행할 수 있는 C#, 실행 속도입니다.
태그가 지정된 메서드 본문만 변경됩니다. 리로드(reload)는 호출 사이트, 필드 또는 타입 레이아웃을 건드리지 않습니다. 새로운 필드 없음, 새로운 클래스 없음. 그 제약 조건이 라이브 인스턴스에 적용해도 안전하게 만드는 것이며, 그것을 염두에 두고 계획해야 합니다. 어떤 메서드를 반복할지 결정하고 태그를 지정하세요. 리로드 can는 새 메서드를 선언하고 리로드된 본문에서 이를 호출할 수 있지만, 그 메서드는 리로드 진입점이 될 수 없습니다. 왜냐하면 다음 실제 컴파일까지는 컴파일된 어떤 것도 그것을 호출할 것을 알지 못하기 때문입니다.
C# 서브셋을 실행합니다. 인터프리터는 수동으로 제작되었고 플레이어 안에 들어가야 하므로 작게 유지되어야 해서 C#의 모든 것을 구현하지는 않습니다. [MethodReload] 본문 내에서는 다음을 사용할 수 없습니다:
await및Task,try/catch/finally,lock, LINQ 쿼리 구문,checkeddecimal,dynamic, 반사 타입, 스레딩 타입ref,out, 및in매개변수,ref반환 및 지역 변수, 다차원 배열(jagged도 괜찮음), 제네릭 메서드
does는 대부분의 게임플레이 코드를 다룹니다: 기본형, 메서드 호출, 루프, 델리게이트, 문자열 보간, 코루틴 yield, Collections. Roslyn 분석기는 패키지와 함께 제공되며 태그가 지정된 본문에 대해서만 위반 사항을 IDE에서 표시하므로 푸시 후에가 아니라 타이핑하는 중에 알 수 있습니다.
기기에서 해석된 코드는 플레이어가 노출하는 것만 호출할 수 있습니다. IL2CPP는 참조가 없는 코드를 제거하므로, 유니티 또는 BCL 멤버를 호출하는 다시 로드된 본문이 빌드에서 사용하지 않은 것이 있으면 호출할 것이 없습니다. 에디터에서 누락된 멤버는 필요할 때 바인딩됩니다. 장치에서 푸시 검사는 앞에서 확인하고 누락된 구성원을 경고로 나열하며, 코드가 어쨌든 하나에 도달하면 예외를 발생시킵니다. 경험칙: 메서드가 무엇을 하는지를 바꾸고, 그것이 의존하는 API 집합은 바꾸지 마십시오.
인터프리터 코드는 컴파일된 코드보다 느립니다. 그것이 인터프리터의 본질이며, 비교가 IL2CPP 사전 컴파일이든 Mono 및 CoreCLR의 JIT이든 마찬가지입니다. 개발 빌드에서는 태그가 지정된 메서드에만 영향을 미칩니다. 릴리스 빌드에는 프롤로그나 인터프리터가 없으며, 태그가 지정된 메서드는 컴파일된 본문만 실행합니다. 궁극적으로, 반복 과정 중 성능 저하가 향상된 반복 속도와 편의성에 그만한 가치가 있는지 여부는 당신이 결정해야 합니다.
코루틴은 생성 시 변경 사항을 감지합니다. [MethodReload] IEnumerator 메서드는 다음에 StartCoroutine이 실행될 때 새 본문을 사용합니다. 이미 실행 중인 코루틴은 시작했을 때의 본문을 유지합니다. [OnMethodReloaded]와 같은 매개변수 없는 인스턴스 메서드에 연결하면, 리로드 후 해당 타입에 도달하는 모든 활성 인스턴스에서 실행되며, 장기간 실행되는 루프를 재시작하기 위한 도메인 리로드 없는 OnEnable을 가질 수 있습니다.
[OnMethodReloaded]
void OnMethodReloaded()
{
StopAllCoroutines();
StartCoroutine(MainLoop());
}내부 작동 방식
두 가지 아이디어의 조합입니다. 첫 번째는 C# 컴파일이 IL 바이트코드를 생성한다는 것이고, IL이 적당한 부분 집합 내에 머무른다면 컴파일되는 대신 인터프리터로 실행될 수 있다는 것입니다. 그 인터프리터 자체도 일반적인 C#이므로 IL2CPP는 그것을 게임의 나머지 부분과 함께 컴파일하며, 이것이 기기에서 실행될 수 있게 해줍니다.
두 번째 아이디어는 그것에 뛰어드는 것입니다. 모든 [MethodReload] 메서드는 인터프리터로 연결된 경로를 가집니다. 인터프리터로 분기하는 것은 컴파일 시간에 태그가 지정된 메서드에 프롤로그를 엮음으로써 수행됩니다. 디컴파일하면 다음과 같습니다:
[MethodReload]
public void TakeDamage(int amount)
{
// weaved during compilation
if (MethodReloadRegistry.HasOverride("Player.TakeDamage") &&
MethodReloadRegistry.TryInvokeMethodReload(
"Player.TakeDamage", this, amount))
{
return;
}
// initial code
hp -= amount;
}직조된 체크는 레지스트리에 이 메서드에 대한 재정의가 있는지 묻습니다. 감시자와 CLI는 해당 레지스트리를 새로 컴파일된 IL로 채웁니다. 엔트리가 있으면 인터프리터가 그것을 실행하고 원본 본문은 건너뜁니다. 그렇지 않으면 컴파일된 코드는 평소처럼 실행됩니다.
이것이 어떻게 비교되는지
C#에서 도메인 리로드 없이 반복 작업을 할 수 있는 좋은 도구들이 이미 존재하며, 우리는 그것들이 적용되는 지점을 가정하는 것보다 정확하게 구분하는 것이 낫습니다.
Asset Store의 패키지가 실행 중인 Mono 런타임에서 패치 메서드를 사용합니다. 그것은 그들에게 훌륭한 에디터 경험을 제공하며, 리로드된 본체에 전체 C#이 포함되고, 태그 지정 단계가 없으며, 핫 리로드의 경우 Mono 플레이어 빌드를 위한 온디바이스 모드를 제공합니다. 모든 실제 이점들 또한, 패치할 런타임이 없기 때문에 IL2CPP 플레이어와 함께 작동하지 않는다는 의미이기도 합니다.
.NET Hot Reload는 Visual Studio 및 Rider에서 .NET 애플리케이션을 위한 기능으로, 런타임의 편집 및 계속 경로를 통해 편집 사항을 적용합니다. 이는 CoreCLR용으로 구축되었으며 Unity Mono 또는 IL2CPP 런타임을 대상으로 하지 않습니다.
MethodReload는 인터프리터 백엔드와 언어 커버리지를 통해 도달 범위에서 다른 위치에 있습니다. C#의 하위 집합이지만, 그렇지 않으면 휴대폰이나 콘솔 개발 키트에 다시 배포해야 하는 동일한 IL2CPP 개발 빌드에서 실행되며, 이미 연결된 케이블을 통해, 플레이어에 추가 서버 없이, 열 포트 없이 실행됩니다.
솔직히 말해서: 만약 반복 작업의 고통이 에디터에 있다면, 기존 도구들은 성숙했으며 당신에게 잘 맞는 것을 계속 사용해야 합니다. 반복 작업의 고통이 장치에 존재하고, 특히 IL2CPP에서 그렇다면, 이것이 거기에 가는 첫 번째 옵션입니다.
최종 팁
지금 프로젝트를 위한 워크플로우를 설정하고 있다면:
- 핫스팟을 태그하세요.
[MethodReload]는 릴리스 빌드에서 비용이 들지 않으므로, 디바이스에서 건드릴 것을 알고 있는Update, 튜닝 및 게임 느낌 메서드를 태그하세요. - 분석기가 당신을 부분 집합 안에 머물게 하십시오. 메서드가
try/catch또는await를 필요로 한다면, 해당 부분을 태그가 없는 헬퍼로 이동하고 주변 로직을 다시 로드하십시오. 분석기가 잘못하고 있는 부분이 있는지 알려줄 것입니다. - 편집기에서 먼저 시도해 보세요. CLI에서
reload_file_editor_interpreter로 실행되는 감시자는 편집 내용을 장치에서 실행되는 것과 동일한 인터프리터로 실행합니다. 그것이 거기서 작동한다면 하드웨어에서도 작동할 것이므로 빌드를 기다리지 않고 서브셋 및 누락된 API 문제를 포착할 수 있습니다.
다음은 무엇인가요
명령어 참조, 런타임 설정 페이지 및 전체 제약 조건 목록은 com.unity.pipeline docs에 있습니다.
이것을 프로젝트에서 사용해 보시길 권장하며 어떻게 진행되는지 알려주세요. 피드백에 크게 감사드리며, 사람들이 밀어붙일 때 실험적인 패키지가 가장 빠르게 개선됩니다.
도메인 리로드는 그것을 필요로 하는 변경 사항에 대해서는 진행되지 않고 있습니다. 변화가 없는 것에 대해서는 기다리기를 멈출 수 있습니다.