포트 & 트리거
포트와 트리거는 엔티티에 속하며, Warudo 스크립팅에서 가장 중요한 요소라고 할 수 있어요. 이들은 엔티티 간에 데이터를 주고받고, 액션을 트리거하며, 에디터에서 사용자와의 상호작용을 제공해요.
데이터 인풋 포트
데이터 인풋 포트는 사용자가 에디터를 통해 제공하는 데이터를 받거나, 다른 엔티티로부터 데이터를 받을 때 사용돼요. 데이터 인풋은 다양한 타입을 가질 수 있는데, 문자열, 숫자, 불리언 같은 기본적인 타입부터 구조화된 데이터나 배열 같은 복잡한 타입도 지원해요.
데이터 인풋은 [DataInput]
속성으로 표시되며, 엔티티 서브클래스의 public 필드로 정의돼요. 시작하기 예제에서 우리는 숫자 슬라이더를 정의하는 DataInput
을 보았죠:
[DataInput]
[IntegerSlider(1, 100)]
public int LuckyNumber = 42;
다양한 예시가 있어요:
- 스트링 인풋:
[DataInput]
public string MyName = "Alice"; - 열거형 인풋: (에디터에서 드롭다운으로 표시)
public enum Color {
Red,
Green,
Blue
}
[DataInput]
public Color MyColor = Color.Red; - 배열 인풋: (에디터에서 수정 가능한 리스트로 표시)
[DataInput]
public float[] MyFavoriteNumbers = new float[] { 3.14f, 2.718f };
아래는 에디터에서 어떻게 보이는지의 예시예요 (이 예에서는 노드를 사용하지만, 에셋과 플러그인에서도 동일하게 적용돼요):
코드에서 지정한 기본값으로 데이터 인풋이 초기화되는 방식에 주목해 주세요.이 기본값들은 사용자가 데이터 인풋 레이블 옆에 있는 "Reset" 버튼을 클릭할 때 다시 할당돼요.
데이터 인풋은 일반적으로 직렬화 가능한 타입을 가집니다. 여기서 "직렬화 가능"이란 Warudo를 종료하고 다시 열었을 때 데이터 값이 저장되고 복원될 수 있음을 의미해요. 다음 타입들이 기본적으로 직렬화 가능해요:
- 기본 타입:
int
,float
,bool
,string
그리고 모든 열거형 타입 - Unity 타입:
Vector2
,Vector3
,Vector4
,Color
- 구조화된 데이터 타입
- 에셋 레퍼런스
- 직렬화 가능한 타입의 배열
노드의 경우, 에디터에서 수정할 수는 없지만 노드 자체에서 처리되는 비직렬화 가능한 데이터 인풋도 정의할 수 있어요. 예를 들어, 다음 코드는 제네릭 오브젝트
데이터 인풋에서 ToString()
메서드를 호출해요:
[NodeType(Id = "dc28819e-5149-4573-945e-40e81e2874c4", Title = "ToString()", Category = "CATEGORY_ARITHMETIC")]
public class ToStringNode : Node {
[DataInput]
public object A; // Not serialized
[DataOutput]
[Label("OUTPUT_STRING")]
public string Result() {
return A?.ToString();
}
}
다른 일반적인 데이터 인풋 타입은 직렬화할 수 없지만 노드 간에 전달할 수 있는 Dictionary
, GameObject
그리고 Quaternion
이 있어요.
속성
데이터 인풋 포트는 속성을 사용해 에디터에 추가적인 정보를 제공할 수 있어요. 예를 들어, 이전에 봤던 [IntegerSlider]
속성은 데이터 인풋이 1에서 100까지의 범위를 가진 슬라이더로 표시되도록 지정해요. 지원되는 속성은 다음과 같아요:
[Label(string label)]
: 데이터 인풋의 라벨을 지정해요.[HideLabel]
: 데이터 인풋의 라벨을 숨기도록 지정해요.[Description(string description)]
: 데이터 인풋의 설명을 지정해요.[HiddenIf(string methodName)]
: 지정된 메서드가true
를 반환하면 데이터 인풋이 숨겨지도록 지정해요. 메서드는 엔티티 클래스의public
또는protected
메서드여야 하며,bool
을 반환해야 해요.[DataInput]
public int MyNumber = 0;
[DataInput]
[HiddenIf(nameof(IsSecretDataInputHidden))]
public string SecretDataInput = "I am hidden unless MyNumber is 42!";
public bool IsSecretDataInputHidden() => MyNumber != 42;[HiddenIf(string dataInputPortName, If @if, object value)]
: 지정된 데이터 인풋 포트가@if
조건을 만족하면 데이터 인풋이 숨겨지도록 지정해요. value는 상수여야 해요.[DataInput]
public int MyNumber = 0;
[DataInput]
[HiddenIf(nameof(MyNumber), If.NotEqual, 42)]
public string SecretDataInput = "I am hidden unless MyNumber is 42!";[HiddenIf(string dataInputPortName, Is @is)]
: 지정된 데이터 인풋 포트가@is
조건을 만족하면 데이터 인풋이 숨겨지도록 지정해요.[DataInput]
public CharacterAsset MyCharacter;
[DataInput]
[HiddenIf(nameof(MyCharacter), Is.NullOrInactive)]
public string SecretDataInput = "I am hidden unless MyCharacter is selected and active!";[DisabledIf(...)]
:[HiddenIf(...)]
와 유사하지만, 데이터 인풋이 숨겨지는 대신 비활성화돼요.[Hidden]
: 데이터 인풋이 항상 숨겨지도록 지정해요. 코드는 사용하지만, 사용자에게 노출되지 않도록 할 때 유용해요.[Disabled]
: 데이터 인풋이 항상 비활성화(편집 불가)되도록 지정해요. 예를 들어, 고정된 길이의 배열 데이터 인풋이거나, 항상 프로그래밍적으로 설정되는 데이터 인풋을 시각화할 때 유용해요.[Section(string title)]
: 새로운 섹션과 함께 데이터 인풋 및 모든 후속 데이터 인풋이 표시되도록 지정해요. 다른 섹션이 지정되지 않는 한 계속 유지돼요.[SectionHiddenIf(string methodName)]
:[Section]
속성이 필요해요. 지정된 메서드가true
를 반환하면 섹션이 숨겨지도록 지정해요. 메서드는public
또는protected
메서드여야 하며,bool
을 반환해야 해요.@if
및@is
조건도 지원해요.[Markdown(bool primary = false)]
: 데이터 인풋이 Markdown 텍스트로 표시되며 편집할 수 없도록 지정해요. 데이터 인풋은string
타입이어야 해요.primary
가true
면, 더 큰 글씨 크기와 배경색 없이 텍스트가 표시돼요.
[HiddenIf]
및 [DisabledIf]
속성은 에셋이나 노드가 편집기에서 보일 때 매 프레임 평가돼요. 따라서 이러한 메서드에서 성능 소모가 큰 연산은 피하는 것이 좋아요.
일부 속성은 특정 데이터 인풋 타입에만 적용돼요:
[IntegerSlider(int min, int max, int step = 1)]
: 데이터 타입이int
또는int[]
이어야 해요. 지정된 범위를 가진 정수 슬라이더로 데이터 인풋을 표시하도록 지정해요.[FloatSlider(float min, float max, float step = 0.01f)]
: 데이터 타입이float
또는float[]
이어야 해요. 지정된 범위를 가진 실수 슬라이더로 데이터 인풋을 표시하도록 지정해요.[AutoCompleteResource(string resourceType, string defaultLabel = null)]
: 데이터 타입이string
이어야 해요. 지정된 리소스 유형의 자동완성 리스트로 데이터 인풋을 표시하도록 지정해요. 예를 들어 "Character → Default Idle Animation" 데이터 인풋은[AutoCompleteResource("CharacterAnimation")]
으로 정의돼요. 자세한 내용은[리소스 프로바이더 및 리졸버]
페이지를 참조하세요.(resource-providers-and-resolvers.md) page for more information.[AutoCompleteList(string methodName, bool forceSelection = false, string defaultLabel = null)]
: 데이터 타입이string
이어야 해요. 지정된 메서드에 의해 생성된 드롭다운 메뉴로 데이터 인풋을 표시하도록 지정해요. 메서드는 엔티티 클래스의public
또는protected
메서드여야 하며,UniTask<AutoCompleteList>
를 반환해야 해요. 메서드는 비동기일 수 있어요. 만약forceSelection
이true
면 사용자는 드롭다운 리스트에서만 값을 선택할 수 있으며, 선택하지 않으면null
값이 할당돼요.using System.Linq;
[DataInput]
[AutoComplete(nameof(AutoCompleteVipName), forceSelection: true)]
public string VipName = "Alice";
protected async UniTask<AutoCompleteList> AutoCompleteVipName() {
return AutoCompleteList.Single(vipNames.Select(name => new AutoCompleteEntry {
label = name, // This is what the user sees
value = name // This is what the field stores
}).ToList());
}
private List<string> vipNames; // Entity-controlled runtime data
// Some other code should update the vipNames list팁자동완성 리스트는 컴파일 시점에 알 수 없는 옵션 목록을 제공할 때 유용해요. 예를 들어, 디렉터리에서 파일 목록을 제공하거나, 원격 서버에서 이모티콘 목록을 제공할 때 사용할 수 있어요. Warudo의 내부 노드 및 에셋에서도 매우 자주 사용돼요!
[MultilineInput]
:데이터 타입이string
이어야 해요. 여러 줄로 구성된 텍스트 입력 필드로 데이터 인풋을 표시하도록 지정해요.[CardSelect]
: 데이터 타입이 열거형이어야 해요. 카드 선택 리스트로 데이터 인풋을 표시하도록 지정해요. (예: "Camera → Control Mode").public enum Color {
[Label("#00FF00")]
[Description("I am so hot!")]
Red,
[Label("#00FF00")]
[Description("I am so natural!")]
Green,
[Label("#0000FF")]
[Description("I am so cool!")]
Blue
}
[DataInput]
[CardSelect]
public Color MyColor = Color.Red;
열거
열거형 타입에서 [Label(string label)]
속성을 사용해 열거형 값의 에디터 라벨을 커스터마이징 할 수 있어요. 예를 들어:
public enum Color {
[Label("#FF0000")]
Red,
[Label("#00FF00")]
Green,
[Label("#0000FF")]
Blue
}
속성 섹션에서 설명한 것처럼 [Description(string description)]
과 [Icon(string icon)]
속성을 사용해 열거형 입력을 카드 목록으로 표시할 수도 있어요.
icon
은 단일 SVG 요소여야 해요(예: <svg>...</svg>
). 예를 들어:
<svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 512 512">
<path>...</path>
</svg>
에셋 레퍼런스
에셋 레퍼런스는 에셋, 노드, 그리고 플러그인이 아닌 구조화된 데이터에서만 사용할 수 있어요.
데이터 입력을 사용해 현재 씬에서 다른 에셋을 참조할 수 있어요. 예를 들어, 아래 코드는 CharacterAsset
을 참조하는 데이터 입력을 정의해요:
[DataInput]
public CharacterAsset MyCharacter;
에디터에서 사용자는 드롭다운 목록에서 캐릭터 에셋을 선택할 수 있어요:
그 후 참조된 에셋의 포트와 트리거에 접근할 수 있어요:
[FlowInput]
public Continuation Enter() {
if (MyCharacter.IsNonNullAndActive()) {
MyCharacter.EnterExpression("Joy", transient: true); // Make the character smile!
}
return Exit;
}
에셋이 null
이거나 비활성 상태인지 확인하려면 asset.IsNullOrInactive()
를 사용하고, 활성 상태인 경우 asset.IsNonNullAndActive()
를 사용할 수 있어요..
에셋을 드롭다운 목록에서 필터링하고 싶다면 어떻게 해야 할까요? [AssetFilter(string methodName)]
속성을 사용해 씬에 있는 에셋을 필터링할 메서드를 지정할 수 있어요. 메서드는 해당 에셋 타입의 파라미터를 받아 bool
값을 반환하는 public
또는 protected
메서드여야 해요. 예를 들어:
[DataInput]
[AssetFilter(nameof(FilterCharacterAsset))]
public CharacterAsset MyCharacter;
protected bool FilterCharacterAsset(CharacterAsset character) {
return character.Active; // Only show active characters
}
프로그래밍 방식으로 데이터 인풋 액세스
엔티티가 있다고 가정해볼게요. 데이터를 읽는 두 가지 방법이 있어요:
- 데이터 인풋 필드에 직접 접근하기. 예를 들어,
public int MyNumber = 42;
라는 퍼블릭 데이터 인풋 필드를 가진 노드가 있다면node.MyNumber
를 사용해MyNumber
의 값을 직접 읽을 수 있어요. T GetDataInput<T>(string key)
또는object GetDataInput(string key)
메서드를 사용하기. 이 메서드는 모든 엔티티에서 사용할 수 있으며, 지정된 이름의 데이터 인풋 값을 반환해요. 예를 들어.MyNumber
라는 데이터 인풋을 가진 노드가 있을 때,node.GetDataInput<int>("MyNumber")
또는 스타일적으로 선호되는node.GetDataInput<int>(nameof(node.MyNumber))
를 사용해MyNumber
값을 읽을 수 있어요.
포트의 키는 필드 이름과 동일하며, 포트가 동적으로 추가된 경우는 제외됩니다(동적 포트 참조).
두 번째 방법은 데이터를 동적으로 접근해야 할 때 유용해요. 예를 들어, 문자열 변수에 따라 데이터를 접근해야 하는 경우에요. (마찬가지로 동적 포트참조.) 그 외에는 두 방법에 큰 차이가 없어요.
마찬가지로, 데이터 인풋에 값을 쓰기 위해서는 데이터 인풋 필드에 직접 값을 할당하거나 void SetDataInput<T>(string key, T value)
메서드를 사용할 수 있어요. 예를 들어, MyNumber
라는 데이터 인풋의 값을 설정하려면 node.MyNumber = 42
또는 스타일적으로 선호되는 node.SetDataInput("MyNumber", 42, broadcast: true)
또는 node.SetDataInput(nameof(node.MyNumber), 42, broadcast: true)
을 사용할 수 있어요.
SetDataInput(nameof(MyNumber), 42, broadcast: true)
대신 다음과 같이 쓸 수 있어요:
MyNumber = 42;
BroadcastDataInput(nameof(MyNumber));
하지만 이 경우에는 두 번째 방법을 사용하는 것이 깅력하게 권장되는데, 이유는 두 가지에요:
- 데이터 인풋의 변경 사항이 관찰자에게 통지된다는 점.
broadcast
파라미터를true
로 설정하면, 변경 사항이 에디터에 전송돼요. 그렇지 않으면BroadcastDataInput(string key)
를 사용해 수동으로 에디터에 변경 사항을 전송해야 해요.
첫 번째 방법은 다음과 같은 경우에만 사용해야 해요:
- 매우 자주 데이터를 업데이트하지만, 매번 에디터에 변경 사항을 전송할 필요가 없을 때. 즉, 가끔
BroadcastDataInput(string key)
를 호출해 성능을 절약할 수 있어요. - 데이터 인풋의 관찰자에게 변경 사항을 통지하고 싶지 않을 때. 이는 드문 경우에요.
데이터 아웃풋 포트
데이터 아웃풋 포트는 다른 노드에 데이터를 제공하는 데 사용돼요. 데이터 아웃풋은 노드 하위 클래스에서 [DataOutput]
속성으로 정의된 공개 메서드로 정의돼요. 이 메서드는 void가 아닌 타입을 반환해야 해요. 아래는 예시예요:
[DataOutput]
public int RandomNumber() {
return Random.Range(1, 100); // Return a random number between 1 and 100
}
데이터 아웃풋은 일부 데이터 인풋 속성을 지원해요: [Label]
, [HideLabel]
, [Description]
, [HiddenIf]
, [DisabledIf]
.
플로우 인풋 포트
플로우 인풋 포트는 다른 노드로부터 플로우 신호를 받아 특정 작업을 실행하는 데 사용돼요. 플로우 인풋은 노드 하위 클래스에서 [FlowInput]
속성으로 정의된 공개 메서드로 정의되며, 해당 메서드는 Continuation
이라는 플로우 아웃풋을 반환해야 해요. 다음은 예시예요:
[DataInput]
public bool FlowToA = true;
[FlowInput]
public Continuation Enter() {
return FlowToA ? ExitA : ExitB; // If FlowToA is true, trigger ExitA; otherwise, trigger ExitB
}
[FlowOutput]
public Continuation ExitA;
[FlowOutput]
public Continuation ExitB;
플로우 인풋은 일부 데이터 인풋 속성을 지원해요: [Label]
, [HideLabel]
, [Description]
. 메서드가 Enter()
로 명명되고 [Label]
속성이 없으면, 에디터의 언어로 'Enter'라는 단어로 자동 설정돼요.
플로우 아웃풋 포트
플로우 아웃풋 포트는 다른 노드로 플로우 신호를 보내는 데 사용돼요. 플로우 아웃풋은 노드 하위 클래스에서 [FlowOutput]
속성으로 정의된 공개 필드로 정의되며, 필드는 Continuation
타입이어야 해요. 플로우 인풋의 예시에서 볼 수 있어요.
플로우 아웃풋은 일부 데이터 인풋 속성을 지원해요: [Label]
, [HideLabel]
,[Description]
. 필드 이름이 Exit
이고 [Label]
속성이 없으면, 에디터의 언어로 'Exit'이라는 단어로 자동 설정돼요.
트리거
트리거는 에디터에서 클릭해 특정 작업을 실행할 수 있는 버튼이에요. 트리거는 엔티티 하위 클래스에서 [Trigger]
속성으로 정의된 공개 메서드로 정의돼요. 아래는 예시예요:
[Trigger]
public void ShowPopupMessage() {
Context.Service.PromptMessage("Title of the message", "Content of the message");
}
에디터에서 다음과 같이 렌더링돼요:
사용자가 버튼을 클릭하면 ShowPopupMessage
메서드가 호출돼요.
트리거는 일부 데이터 인풋 속성을 지원해요: [Label]
, [HideLabel]
, [Description]
, [HiddenIf]
, [DisabledIf]
, [Section]
, [SectionHiddenIf]
.