리소스 프로바이더와 리졸버
Warudo에서 리소스는 에셋과 노드에서 사용할 수 있는 외부 데이터를 의미해요. 예를 들어, 캐릭터 리소스는 .vrm
및 .warudo
파일이며, 이는 Characters
디렉토리에 저장돼요. 캐릭터 애니메이션 리소스는 Warudo에서 제공하는 500개 이상의 기본 애니메이션과 CharacterAnimations
디렉토리에 있는 커스텀 캐릭터 애니메이션 모드를 포함해요. 스크린 이미지 리소스는 Images
디렉토리에 있는 이미지 파일 등이 있어요.
개요
내부적으로, 각 리소스는 resource URI로 고유하게 식별되며, 이런 식으로 나타나요: character://data/Characters/MyModel.vrm
또는 character-animation://resources/Animations/AGIA/01_Idles/AGIA_Idle_generic_01
.
캐릭터 에셋의 Source
드롭다운과 같은 리소스 드롭다운을 열면, 드롭다운이 리소스 제공자 를 쿼리해 호환 가능한 resource URI 목록을 받아요. 캐릭터 에셋의 경우, 두 개의 리소스 제공자가 결과를 반환해요: 하나는 Characters
디렉토리에서 파일을 찾고, 다른 하나는 Steam Workshop에서 설치된 캐릭터 모드를 찾아요.
리소스 URI를 선택하면 Warudo는 해당 리소스 URI 리졸버를 호출해 리소스 데이터를 로드해요. 예를 들어, .vrm
파일과 .warudo
파일은 각각 다른 리졸버가 로드하지만, 두 리졸버는 모두 GameObject
를 반환해요(Unity 씬에 로드된 캐릭터). 따라서 캐릭터 에셋은 이 둘을 동일하게 처리할 수 있어요.
리소스 프로바이더와 리졸버는 반드시 플러그인에 의해 등록돼야 해요.
프로바이더
큐브와 구체 같은 소품 리소스를 제공하는 커스텀 리소스 프로바이더를 등록하는 토이 플러그인 예제를 살펴볼게요.
using System;
using System.Collections.Generic;
using Warudo.Core;
using Warudo.Core.Attributes;
using Warudo.Core.Plugins;
using Warudo.Core.Resource;
[PluginType(
Id = "hakuyatira.primitiveprops",
Name = "Primitive Props",
Description = "A simple plugin that registers primitive props.",
Version = "1.0.0",
Author = "Hakuya Tira",
SupportUrl = "https://docs.warudo.app")]
public class PrimitivePropsPlugin : Plugin {
protected override void OnCreate() {
base.OnCreate();
Context.ResourceManager.RegisterProvider(new PrimitivePropResourceProvider(), this);
}
}
public class PrimitivePropResourceProvider : IResourceProvider {
public string ResourceProviderName => "Primitives"; // The name of your provider
public List<Resource> ProvideResources(string query) {
if (query != "Prop") return null; // If the query is not "Prop", we don't have any compatible resources
return new List<Resource> {
new Resource {
category = "Primitives", // Category that will be shown in the dropdown
label = "Cube", // Label that will be shown in the dropdown
uri = new Uri("prop://primitives/cube") // Underlying resource URI
},
new Resource {
category = "Primitives",
label = "Sphere",
uri = new Uri("prop://primitives/sphere")
}
};
}
}
위 예제에서, 우리는 두 가지 소품 리소스(큐브와 구체)를 제공하는 커스텀 리소스 프로바이더를 등록해요. ProvideResources
메서드는 쿼리가 "Prop"
일 때만 리소스 목록을 반환하는데, 이는 소품 에셋이 "Prop"
이라는 쿼리로 소품 리소스를 요청하기 때문이에요. 예시는 다음과 같아요:
// In the prop asset class
[AutoCompleteResource("Prop")]
public string Source;
내장 에셋에서 사용하는 쿼리 목록은 내장 리소스 타입 섹션에서 확인할 수 있어요.
prop://primitives/cube
와 같은 방식으로 URI를 작성하는데, 소품 리소스에는 prop://
스킴을 사용하는 것이 관례지만, 반드시 이를 따를 필요는 없어요. 특히, 리소스 URI에 대한 커스텀 리졸버를 작성하는 경우에는 더 자유롭게 작성할 수 있어요.
플러그인이 로드된 후, 소품 에셋의 Source
드롭다운을 열면, 우리가 커스텀 리소스 프로바이더로 제공한 두 가지 리소스를 확인할 수 있을 거예요.
이 리소스를 선택하면 해당하는 리소스 URI 리졸버가 호출되어 소품 데이터를 로드하게 돼요. 하지만 아무도 우리의 URI를 어떻게 해결할지 모르니, 이제 URI를 해결할 리졸버를 만들어볼게요.
URI 리졸버
다음 클래스를 플러그인에 추가해 주세요:
public class PrimitivePropResourceUriResolver : IResourceUriResolver {
public object Resolve(Uri uri) {
if (uri.Scheme != "prop" || uri.Authority != "primitives") return null;
var path = uri.LocalPath.TrimStart('/');
return path switch {
"cube" => GameObject.CreatePrimitive(PrimitiveType.Cube),
"sphere" => GameObject.CreatePrimitive(PrimitiveType.Sphere),
_ => throw new Exception("Unknown primitive prop: " + path)
};
}
}
첫 두 줄은 URI가 우리의 커스텀 형식인 prop://primitives/xxx
와 일치하는지 확인해요. 일치하는 경우, URI의 마지막 부분(path
)을 추출하여 큐브나 구체를 생성해요. 소품 에셋이 소품 리소스를 선택할 때 기대하는 것이 GameObject
이므로 게임오브젝트를 직접 반환해요.
반환되는 오브젝트 타입은 해당 리소스를 사용하는 에셋과 호환되어야 해요. 예를 들어, 캐릭터 에셋은 캐릭터 리소스를 선택할 때 GameObject
를 기대하고, 화면 에셋은 이미지 리소스를 선택할 때 ImageResource
를 기대해요. 내장 에셋에서 기대하는 타입 목록은 내장 리소스 타입 섹션에서 확인할 수 있어요.
이제 플러그인의 OnCreate
메서드에서 리졸버를 등록하기만 하면 돼요:
Context.ResourceManager.RegisterUriResolver(new PrimitivePropResourceUriResolver(), this);
플러그인이 다시 로드되면, 소품 에셋에서 큐브나 구체를 선택할 때 Warudo가 Unity 씬에 큐브나 구체를 생성할 거예요!
모드 컬렉션
리소스 프로바이더와 리졸버의 일반적인 사용 사례 중 하나는 모드 컬렉션을 제공하는 거예요. 예를 들어, Unity에 100개의 소품 프리팹이 있고 이를 Warudo에서 사용하고 싶다면, 100개의 소품 모드를 Props
디렉토리에 익스포트하는 대신, 플러그인의 모드 폴더에서 프리팹을 직접 로드하는 커스텀 리소스 프로바이더와 리졸버를 작성할 수 있어요(자세한 내용은 로딩 Unity 에셋 참고). 이렇게 하면 모든 리소스를 드롭다운에서 동일한 카테고리로 볼 수 있는 추가 이점도 얻을 수 있어요.
모드 컬렉션 플러그인을 작성하는 모범 사례는 카타나 애니메이션 샘플 플러그인을 참고하세요.