+
+## 뼈대 스케일 조정
+
+캐릭터의 머리를 부풀리기 위해서는 캐릭터의 뼈대 중 일부를 확대해야 해요. Warudo(또는 어떤 Unity 앱/게임에서도) 캐릭터는 55개의 뼈대를 가지고 있으며, 그중 일부는 선택 사항이에요. 우리가 관심 있는 머리 뼈대는 "Head"로 불립니다. **Set Character Bone Scale** 노드를 노드 에디터에 추가하고, **Bone**을 "Head"로 설정한 후, **Scale**을 1.1로 설정해보세요.
+
+:::tip
+기본적으로 Scale 옵션을 설정하면 XYZ 축이 동일한 비율로 조정됩니다. 이를 균일 스케일링이라고 불러요. 각 축을 개별적으로 조정하고 싶다면 **Uniform Scaling** 버튼을 클릭해 해제할 수 있습니다.
+:::
+
+![](/doc-img/en-blueprint-balloon-1.png)
+
+이제 Enter를 클릭해 노드를 테스트해보세요. 캐릭터의 머리가 조금 더 커지는 것을 볼 수 있을 거예요!!
+
+## 얼굴 추적 데이터 읽기
+
+이제 머리 뼈대의 스케일을 조정할 수 있으니, 언제 스케일을 조정할지 정해야 해요. 볼을 부풀리는 재미있는 방법으로 스케일을 트리거할 수 있습니다!
+
+볼을 부풀리는지 알아내기 위해서는 **Get Character BlendShape** 노드를 사용할 수 있어요. 노드 에디터에 추가해봅시다:
+
+![](/doc-img/en-blueprint-balloon-2.png)
+
+여기서 **BlendShape** 옵션은 볼을 부풀리는 블렌드셰이프를 선택해야 해요. ARKit 호환 캐릭터 모델을 사용하는 경우, 해당 블렌드셰이프는 보통 "cheekPuff" 또는 "CheekPuff"라고 불려요.
+
+:::tip
+만약 ARKit 호환 캐릭터 모델을 사용하지 않거나 iPhone으로 얼굴을 추적하지 않는다면, cheek puff 블렌드셰이프나 트래킹 데이터를 사용할 수 없어요. 그런 경우, 입을 이용해 트리거를 바꿀 수 있어요. 예를 들어, "jawOpen"(ARKit 블렌드셰이프가 있는 모델) 또는 "A"(VRM 블렌드셰이프만 있는 모델) 블렌드셰이프를 사용해 머리 부풀리기를 트리거할 수 있어요.
+:::
+
+**Inspect Value** 노드를 사용해 블렌드셰이프의 값을 아래와 같이 확인할 수 있어요:
+
+![](/doc-img/en-blueprint-balloon-3.png)
+
+볼을 부풀려 보세요, 그러면 값이 0(볼을 부풀리지 않은 상태)에서 1(볼을 부풀린 상태) 사이에서 변동하는 것을 볼 수 있을 거예요.
+
+이제 노드를 연결할 준비가 되었어요. BlendShape 값을 **Set Character Bone Scale → Scale**에 연결하면 되겠죠? 잠깐만요...
+
+![](/doc-img/en-blueprint-balloon-4.png)
+
+Warudo가 노드를 연결하지 못하게 하고 있어요! 무슨 일이 일어난 걸까요? **Get Character BlendShape → Value**는 소수를 출력하지만, **Set Character Bone Scale → Scale** 옵션은 세 개의 숫자 X, Y,, Z 에요! 좀 더 정확히 말하자면, 우리는 0~1의 소수값을 _벡터_ 로 만들어야해요. (벡터는 단순히 여러 개의 숫자를 멋지게 용어입니다.) 이를 위해 **Scale Vector** 노드를 사용할 수 있어요:
+
+![](/doc-img/en-blueprint-balloon-7.png)
+
+이 노드는 Vector3(즉, 세 개의 소수: X, Y, Z)와 float(소수) 를 입력받아 스케일링된 Vector3를 출력해요. 예를 들어, 입력 값이 (1, 2, 3)과 5라면, 출력은 (5, 10, 15)가 됩니다. 만약 입력 값이 (2, 2, 2)와 0.5라면, 출력은 (1, 1, 1)이 되겠죠.
+
+일단 **Vector3** 옵션을 (2, 2, 2)로 설정하고, 위처럼 노드를 연결한 다음 볼을 부풀렸을 때 어떤 일이 일어나는지 확인해 봅시다.
+
+엥, 아무 일도 일어나지 않나요...? 그 이유는 Get Character Bone Scale 노드가 트리거되지 않았기 때문이에요! 지금은 그냥 수동으로 트리거해 봅시다. 볼을 다시 부풀리고, 이번에는 **Get Character Bone Scale → Enter**를 클릭하세요. 캐릭터의 머리가 커지는 것을 볼 수 있을 거예요!
+
+![](/doc-img/en-blueprint-balloon-5.png)
+
+하지만 문제가 하나 있어요: 볼을 부풀리는 것을 멈추고 다시 **Get Character Bone Scale → Enter**를 클릭하면, 캐릭터의 머리가 이렇게 되어버릴 거예요:
+
+![](/doc-img/en-blueprint-balloon-6.png)
+
+:::tip
+다음 내용을 읽기 전에, 왜 이런 일이 발생하는지 추측해 보세요.
+:::
+
+## 산술 소개
+
+잠시 뒤로 물러서서 우리가 지금 무엇을 하고 있는지 생각해 봅시다. 우리는 cheek puff 블렌드셰이프의 값을 사용하고 있어요. 이 값은 0과 1 사이의 소수인데, 이를 사용해 (2, 2, 2)라는 세 숫자를 스케일하고 있어요. 그렇다면 이 세 숫자의 최대값과 최소값은 어떻게 될까요?
+
+최대값은 (2, 2, 2)이 되어, 캐릭터의 머리가 원래 크기의 두 배가 됩니다. 하지만 최소값은 (0, 0, 0)이 되어, 캐릭터의 머리가 사라져 버려요! 그래서 우리가 볼을 부풀리는 것을 멈추면 캐릭터의 머리가 사라지는 거죠.
+
+이를 해결하려면, 최소값이 **(1, 1, 1)**이 되도록 해서 캐릭터의 머리가 사라지지 않게 해야 해요. 어떻게 할 수 있을까요? 간단히 각 숫자에 항상 1을 더하는 방식으로 해결할 수 있어요. 이를 위해 **Offset Vector3** 노드를 사용할 수 있어요:
+
+![](/doc-img/en-blueprint-balloon-8.png)
+
+여기서 **Scale Vector3 → Vector3**를 (1, 1, 1)로 변경하고 **Offset Vector3 → Offset**을 (1, 1, 1)로 설정했어요. 이제 볼을 부풀리고 Enter를 클릭하면 캐릭터의 머리가 커지고, 다시 볼을 부풀리지 않은 상태에서 Enter를 클릭하면 캐릭터의 머리가 원래 크기로 돌아올 거예요.
+
+이 방법이 왜 효과가 있는지 살펴볼게요. 볼을 부풀릴 때 최대값은 (1, 1, 1) + (1, 1, 1) = (2, 2, 2)가 되고, 최소값은 (0, 0, 0) + (1, 1, 1) = (1, 1, 1)가 됩니다. 바로 우리가 원하는 결과죠!
+
+## 업데이트 {#on-update}
+
+Get Character Bone Scale 노드를 수동으로 트리거하는 게 저만큼이나 귀찮았기를 바랐어요. 실제로 매번 캐릭터의 머리를 부풀릴 때마다 blueprint를 클릭할 수는 없겠죠. 자동으로 노드를 트리거할 방법이 있다면 좋을 텐데... 잠깐만요, 정말 방법이 있네요?
+
+![](/doc-img/en-blueprint-balloon-9.png)
+
+**On Update** 노드는 Warudo가 실행 중일 때 매 프레임마다 트리거되는 이벤트 노드예요. 예를 들어, Warudo가 60 FPS로 실행 중이라면, On Update 노드는 초당 60번 트리거됩니다. 덕분에 초당 60번 클릭하지 않아도 되니 다행이죠. 휴.
+
+:::tip
+Get Character Bone Scale 노드 이후에 노드를 추가하여 볼을 부풀릴 때 더 많은 효과를 트리거할 수 있다는 점을 기억하세요! **Set Character BlendShape** 노드를 추가해 볼을 부풀릴 때 캐릭터의 블렌드셰이프를 활성화해 보세요. 힌트: Set Character BlendShape → Target Value는 0과 1 사이의 소수이므로, Get Character BlendShape → Value를 직접 연결할 수 있어요.
+:::
+
+## 뼈대 스케일 초기화 {#resetting-bone-scale}
+
+이제 이 blueprint를 사용해 스트림 중에 캐릭터의 머리를 부풀릴 수 있게 되었지만, 이 효과를 항상 활성화하고 싶지는 않을 거예요. 그래서 이벤트 노드가 비활성화된 blueprint에서는 트리거되지 않으므로, 필요할 때만 blueprint를 활성화하고 이후에 비활성화하기로 결정했을 거예요.
+
+하지만 작은 문제가 있어요. blueprint를 비활성화할 때 머리 뼈대의 스케일이 (1, 1, 1)이 아닐 수 있어요. 그 순간에 볼을 부풀리고 있었을 수도 있잖아요! 이를 해결하기 위해 **On Disable Blueprint** 노드를 사용해 봅시다:
+
+![](/doc-img/en-blueprint-balloon-10.png)
+
+On Disable Blueprint 노드는 blueprint가 비활성화될 때 트리거되는 특수 노드예요. 이제 blueprint를 비활성화할 때, 머리 뼈대의 스케일이 (1, 1, 1)로 초기화됩니다. 문제 해결!
+
+## 얼굴 추적 데이터 읽기에 대한 추가 내용
+
+:::info
+이 섹션은 고급 사용자를 위한 내용이에요. 이해가 어렵다면 건너뛰어도 괜찮아요.
+:::
+
+이 튜토리얼에서 **Get Character BlendShape** 노드를 사용해서 얼굴 추적 데이터를 읽는 방법을 배웠는데, 사실 우리가 읽고 있는 건 진짜 얼굴 추적 데이터가 아니라, 추적 데이터를 기반으로 캐릭터에 적용된 블렌드셰이프 값이에요. This is a nuanced but important distinction. 이 차이는 작지만 꽤 중요한 부분이에요. 예를 들어, iFacialMocap에서 제공되는 원래 `cheekPuff` 블렌드셰이프 값이 0.5라고 가정해볼게요. 그런데 iFacialMocap Receiver → Configure BlendShapes Mapping에서 `cheekPuff` 값을 2배로 설정했다면, 캐릭터에 적용되는 값은 1.0이 돼요. 즉, 우리가 볼을 절반만 부풀려도 캐릭터의 머리는 최대 크기로 부풀게 되는 거죠. 또한 **Character → Motion Capture**가 비활성화된 경우, 우리가 볼을 부풀리고 있어도 캐릭터의 `cheekPuff` 블렌드셰이프 값은 항상 0이 될 거예요.
+
+대부분의 경우 이렇게 해도 괜찮지만, 만약 실제 얼굴 추적 데이터를 기반으로 작업하고 싶다면, **Get Receiver/Tracker Data** 노드를 사용하는 게 좋아요. 예를 들어 iFacialMocap을 사용 중이라면, **Get iFacialMocap Receiver Data** 노드의 **BlendShape List Get BlendShape** 노드를 통해 실제 `cheekPuff` 값을 가져올 수 있어요:
+
+![](/doc-img/en-blueprint-balloon-11.png)
+
+이 방법의 단점은 iFacialMocap을 사용하지 않으면 이 blueprint가 작동하지 않는다는 점이에요. 하지만 좋은 점은 캐릭터에 `cheekPuff` 블렌드셰이프가 없어도, 우리가 볼을 부풀리는 동작을 다양한 재미있는 효과의 트리거로 사용할 수 있다는 거예요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/buried.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/buried.md
new file mode 100644
index 0000000..97c9046
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/buried.md
@@ -0,0 +1,86 @@
+---
+sidebar_position: 30
+---
+
+# 인형에 파묻히기
+
+혼란을 만드는 것은 즐거움을 만드는거죠. 이 튜토리얼에서는 많은 동물 인형을 생성하고, 캐릭터를 그 밑에 파묻는 방법을 배울 거예요.
+
+
+
동물 인형에 파묻히는 모습.
+
+## 여러 Blueprints
+
+이전 튜토리얼을 따라왔다면, 각 상호작용마다 새로운 blueprint를 생성해왔다는 것을 알 수 있을 거예요. 그렇다면, 모든 노드를 하나의 blueprint에 추가하는게 더 간단하지 않을까요?
+
+기술적으로 말하자면, 여러 blueprint로 할 수 있는 작업은 하나의 blueprint로도 가능해요. 하지만 편성을 유지하는 건 좋은 습관이에요; 여러 개의 작은 blueprint를 사용하면 특정 상호작용을 일시적으로 비활성화해도 다른 상호작용에 영향을 주지 않죠. 또한, blueprint를 다른 사람과 공유하려 할 때, 불필요한 노드가 잔뜩 있는 blueprint보다는 하나의 깔끔한 blueprint를 공유하는 것이 훨씬 쉬워요.
+
+또한 성능도 고려해야 해요. Warudo 에디터는 많은 노드를 처리하는 데 최적화되어 있지 않아요. 하나의 blueprint에 100개 이상의 노드가 있으면 지연 현상이 나타날 수 있어요. 따라서 가능하다면 여러 개의 작은 blueprint로 나누는 것이 항상 좋은 방법이에요.
+
+## 루프용
+
+좋아요, 이제 blueprint를 만들어봅시다! 동물 인형을 생성하는 것은 간단해요: 이미 [익숙한](ragdoll.md) **Throw Prop At Character** 노드를 사용하면 돼요. 하지만 여러 개의 인형을 어떻게 생성할까요?
+
+그 답은 **For Loop** 노드에 있어요. 이름에서 유추할 수 있듯이, 이 노드를 사용하면 여러 번 반복해서 어떤 작업을 수행할 수 있어요. 하지만 자세한 설명은 나중에 다루기로 하고, 먼저 아래의 blueprint를 따라 만들어보세요. For Loop 노드에서는 **Last Index**를 100으로, **Interval**을 0.01로 설정했어요; **Throw Prop At Character** 노드에서는 **Prop Source**를 Tiger로 **From**을 Above Character Head로 설정해 호랑이 인형이 캐릭터의 머리 위로 떨어지게 했어요.
+
+![](/doc-img/en-blueprint-buried-1.png)
+
+우리가 이미 알고 있는 두 노드의 역할은 다음과 같아요: On Keystroke Pressed 노드는 Ctrl+Shift+Z 단축키를 정의하고, Throw Prop At Character 노드는 호랑이 인형을 캐릭터에게 던져요. 하지만 For Loop 노드는 무슨 역할을 할까요? Ctrl+Shift+Z를 눌러서 확인해봅시다.
+
+
+
아야.
+
+호랑이 인형이 많이 생성됐죠! 그런데 몇 개일까요? For Loop 노드를 자세히 살펴보면, 이 노드는 간단히 말해서 다음과 같은 의미예요: "**First Index** (1) 부터 **Last Index** (100)까지 각 숫자에 대해, 0.01초 **Interval**로 **Loop Body** 플로우를 실행한다." 이 Loop Body 플로우가 Throw Prop At Character 노드에 연결되어 있기 때문에, 이는 곧 "1부터 100까지 각 숫자에 대해, 0.01초마다 호랑이 인형을 생성해서 캐릭터에게 던진다"는 의미예요. 또는 더 간단히 말하면, "1초 동안 100개의 호랑이 인형을 생성하여 캐릭터에게 던진다"는 거죠.
+
+![](/doc-img/en-blueprint-buried-3.png)
+
+:::tip
+**Last Index**를 1000으로 설정하면 무슨 일이 일어날지 짐작할 수 있나요? 그리고 **Interval**을 0으로 설정하면 어떻게 될까요?
+:::
+
+캐릭터를 인형더미 속에 묻으려면, 캐릭터를 래그돌로 변환해야 해요. 이미 알고 있는 방법대로, **Activate Character Ragdoll** 노드를 사용하면 돼요. 여기서는 **Launch Force**를 (0, -100, 0)으로 설정하여 캐릭터를 아래로 밀어내려요.
+
+![](/doc-img/en-blueprint-buried-4.png)
+
+## 인행 랜덤화
+
+지금까지는 잘 진행되었지만, 한 가지 문제가 있어요: 모든 인형이 호랑이라는 점이에요. 만약 여러 종류의 동물을 생성하고 싶다면 어떻게 할까요?
+
+**Throw Prop At Character** 노드를 살펴보면, **Prop Source**를 Tiger로 설정했지만, 그 옆에 검은 점이 보이죠? 이 점은 무언가를 연결할 수 있다는 뜻이에요. 즉, 우리가 직접 드롭다운에서 소품을 선택하는 대신, 소품을 생성할 노드를 연결할 수 있다는 거예요. 그리고 바로 그 역할을 하는 것이 **Get Random Prop** 노드예요.
+
+![](/doc-img/en-blueprint-buried-2.png)
+
+위에서 우리는 Get Random Prop 노드의 **Prop Source** 데이터 아웃풋을 Throw Prop At Character 노드의 **Prop Source** 데이터 인풋에 연결했어요. Get Random Prop 노드는 말 그대로 **Props** 목록이나 **Collections** 목록에 있는 소품 컬렉션에서 무작위로 소품을 선택해요. 이 경우, 우리는 Quirky Animals 컬렉션을 **Collections** 목록에 추가했으므로, Get Random Prop 노드는 동물 인형 소품을 무작위로 선택하고, 그것을 Throw Prop At Character 노드로 출력해요.
+
+이제 Ctrl+Shift+Z를 다시 눌러서 어떻게 되는지 확인해봅시다. 잘 설정되었다면, 다양한 동물들이 캐릭터의 머리 위로 떨어지며 캐릭터를 그 아래에 파묻을 거예요. 재미있는 부분이죠!
+
+:::info
+중요한 기술적 세부 사항에 관심이 있다면, 노드의 데이터 인풋은 해당 노드가 트리거될 때 연결된 데이터 아웃풋에서 값을 가져온다는 점이에요. 즉, 이번 경우에는 Throw Prop At Character 노드가 트리거될 때마다 Get Random Prop 노드도 함께 트리거되어, 매번 다른 소품을 출력하는 거예요(이는 For Loop 노드에 의해 발생).
+:::
+
+## 루프에 대한 추가 설명
+
+For Loop 노드에는 **Loop Index** 데이터 아웃풋이 있다는 점을 눈치챘을 거예요. 이 데이터 아웃풋은 루프의 현재 인덱스를 제공하며, **First Index**에서 시작해 **Last Index**에서 끝나요. 위의 예시에서는 **Loop Index** 데이터 아웃풋이 1부터 100까지의 숫자를 제공해요. 이 숫자를 어떻게 사용할 수 있을까요?
+
+여기 하나의 아이디어가 있어요: **Loop Index**를 Get Random Prop 노드의 **Scale** 데이터 인풋에 연결해 보세요. 어떤 일이 일어날지 추측할 수 있나요?
+
+![](/doc-img/en-blueprint-buried-5.png)
+
+정확하게 추측했다면, 떨어지는 인형들이 점점 더 커지는 모습을 보게 될 거예요. 멋지죠!
+
+:::tip
+만약 인형들을 점점 더 작아지게 하고 싶다면 어떻게 해야 할까요? 또는 1에서 100까지의 스케일 대신 0.1에서 10까지의 스케일을 원한다면요? 스스로 한 번 해결해 보세요! **Float Subtraction**과 **Float Multiplication** 노드가 도움이 될 수 있어요.
+:::
+
+**For Loop** 노드에는 **On Loop End**와 **Exit** 플로우 아웃풋도 있어요. **On Loop End** 플로우 아웃풋은 루프가 끝날 때 트리거되고, **Exit** 플로우 아웃풋은 루프가 시작된 직후에 트리거돼요. 아래와 같이 **Show Toast** 노드를 두 개 추가하고 무슨 일이 일어나는지 확인해 보세요.
+
+![](/doc-img/en-blueprint-buried-6.png)
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/camera.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/camera.md
new file mode 100644
index 0000000..1f14b4b
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/camera.md
@@ -0,0 +1,142 @@
+---
+sidebar_position: 70
+---
+
+# 카메라 애니메이션
+
+캐릭터로 충분히 재미를 즐겼으니, 이제 스트림에 영화 같은 카메라 움직임을 추가하는 방법을 알아봅시다. 이번 튜토리얼에서는 간단하지만 효과적인 카메라 애니메이션을 추가하는 방법을 배울 거예요.
+
+:::tip
+Warudo Pro 사용자는 [디렉터](../../assets/director) 에셋을 사용해 카메라 애니메이션을 만들 수도 있어요.
+:::
+
+
+
캐릭터가 춤을 추며, 카메라도 함께 움직입니다!!
+
+## Orbit Character 카메라 애니메이션
+
+이번 튜토리얼의 주인공은 **Camera Orbit Character** 노드입니다. 노드를 노드 에디터로 끌어와 자세히 살펴보죠:
+
+![](/doc-img/en-blueprint-camera-1.png)
+
+Camera Orbit Character 노드는 Orbit Character 제어 모드에서 카메라의 파라미터를 기록하고 재생해요. 마우스를 사용해 카메라를 움직이고, 노드가 카메라의 이동 위치, 줌 인/아웃, 팬 동작 등을 기록하게 할 수 있어요. 이후 언제든지 노드를 트리거하여 카메라가 기록된 위치와 방향으로 이동하게 만들 수 있죠.
+
+:::info
+아직 설정하지 않았다면, **Camera → Control Mode**를 **Orbit Character**로 설정하세요.
+:::
+
+한번 시도해봅시다! 예를 들어, 시청자들에게 캐릭터의 폭신한 꼬리를 보여주고 싶다면, 카메라를 캐릭터의 뒷면으로 회전시켜보세요:
+
+![](/doc-img/en-blueprint-camera-2.png)
+
+그 다음, 노드에서 **Align Target With Main Camera** 버튼을 클릭하면, 노드는 카메라의 위치와 방향을 **Target** 데이터 인풋에 기록합니다. 이제 카메라를 다시 앞으로 돌릴 수 있지만, 언제든지 이 노드를 트리거하면 카메라는 캐릭터의 뒤로 이동하여 꼬리를 보여줄 거예요!
+
+아래처럼 간단한 blueprint를 만들 수 있어요:
+
+![](/doc-img/en-blueprint-camera-3.png)
+
+이렇게 하면 B를 눌러 언제든지 캐릭터의 앞과 뒤를 전환할 수 있어요!
+
+
+
+
+이제 여러분의 차례입니다! 자신만의 카메라 blueprint를 만들어 보고, 캐릭터를 위한 흥미로운 카메라 움직임을 시도해 보세요.
+
+:::tip
+**Transition Time**과 **Transition Easing** 옵션을 가지고 놀면서 카메라 움직임에 어떤 영향을 주는지 확인해 보세요! Transition Easing에서 **InOutSine**은 자연스러운 카메라 움직임을 위한 좋은 출발점입니다.
+:::
+
+## 애니메이션 반복하기
+
+모든 것이 잘 작동하지만 한 가지 문제가 있어요: 카메라 애니메이션을 수동으로 트리거해야 한다는 점입니다. 카메라가 자동으로 움직이게 할 방법이 있을까요?
+
+당연히 방법이 있어요! 어떻게 설정할 수 있는지 살펴봅시다. 먼저, 두 개의 Camera Orbit Character 노드를 설정하고, 각각 다른 카메라 위치와 방향을 지정해 보세요. 제가 첫 번째 노드에 설정한 것은 다음과 같아요:
+
+![](/doc-img/en-blueprint-camera-4.png)
+
+그리고 두 번째 노드의 설정은 다음과 같아요:
+
+![](/doc-img/en-blueprint-camera-5.png)
+
+첫 번째 노드를 트리거하면 카메라가 캐릭터의 왼쪽으로 이동하고, 두 번째 노드를 트리거하면 카메라가 오른쪽으로 이동합니다. 이제 카메라가 왼쪽으로 먼저 이동한 후 오른쪽으로 이동하게 만들려면, **On Transition End** 에서 **Enter**로 아래와 같이 연결하기만 하면 돼요:
+
+![](/doc-img/en-blueprint-camera-13.png)
+
+:::info
+여기서 중요한 점은 **Exit**가 아닌 **On Transition End**에서 연결한다는 것이에요. 이는 Exit 플로우 아웃풋은 노드가 트리거될 때, 즉 카메라가 움직이기 시작할 때 트리거되지만, On Transition End는 카메라가 움직임을 완료한 후에 트리거되기 때문이에요.
+:::
+
+이제 왼쪽 Camera Orbit Character 노드의 Enter 플로우 인풋을 클릭하면 카메라가 캐릭터의 왼쪽으로 이동한 다음, 자동으로 오른쪽으로 이동하는 것을 볼 수 있어요. 여기까지는 잘 작동하네요! 그럼 이제 무엇을 해야 할까요?
+
+당신은 "그건 쉽죠,"라고 말할 수 있어요. "오른쪽 Camera Orbit Character 노드의 On Transition End를 왼쪽 Camera Orbit Character 노드의 Enter로 연결하면 되잖아요!"
+
+하지만 그렇게 하면:
+
+![](/doc-img/en-blueprint-camera-6.png)
+
+오류 메시지가 나타날 거예요!
+
+![](/doc-img/en-blueprint-camera-7.png)
+
+이 연결을 추가할 수 없는 이유는 blueprint에 루프를 만들게 되기 때문이에요. Warudo가 무한 루프에 빠져 멈추는 것을 방지하기 위한 조치예요.
+
+:::info
+만약 루프를 허용했다면, 두 노드의 Transition Time이 0으로 설정되어 있을 경우 카메라가 너무 빠르게 반복해서 이동해 Warudo가 멈추거나 충돌할 수 있어요!
+:::
+
+그러나 **functions**를 사용하여 이 문제를 해결할 수 있어요. Function은 blueprint 내 어디서든 트리거할 수 있는 진입점이며, 다른 blueprint에서도 호출할 수 있어요. 예시로 알아봅시다. **Define Function** 노드를 왼쪽 Camera Orbit Character 노드의 왼쪽에 추가하고 function **Name**을 `AnimateCamera`로 설정하세요:
+
+![](/doc-img/en-blueprint-camera-8.png)
+
+우리는 방금 function을 정의했어요! 이제 `AnimateCamera` function이 트리거되면, 첫 번째 Camera Orbit Character 노드를 트리거하게 됩니다. 그렇다면 이 `AnimateCamera` function을 어떻게 트리거할까요? **Flow Function** 노드를 사용하면 됩니다! 노드를 노드 에디터에 추가하고, 드롭다운에서 방금 만든 function을 선택한 후, Enter 플로우 인풋을 클릭해 보세요:
+
+![](/doc-img/en-blueprint-camera-9.png)
+
+그러면 카메라가 캐릭터의 왼쪽으로 이동한 후, 자동으로 오른쪽으로 이동하는 것을 볼 수 있을 거예요. 이로써 function이 제대로 작동하는 것을 확인할 수 있어요. 이제 마지막으로, 오른쪽 Camera Orbit Character 노드가 움직임을 끝냈을 때 이 function을 자동으로 트리거하도록 설정해봅시다. **Camera Orbit Character → On Transition End**를 **Flow Function → Enter**로 연결하세요:
+
+![](/doc-img/en-blueprint-camera-10.png)
+
+이제 **Flow Function → Enter**를 다시 클릭하면, 카메라가 캐릭터의 왼쪽으로 이동한 후, 자동으로 오른쪽으로 이동하고, 다시 자동으로 왼쪽으로 이동하는 것이 반복됩니다. 바로 우리가 원하는 대로 작동하는 거죠!
+
+주의할 점은, Camera Orbit Character 노드로 생성된 카메라 애니메이션은 우리가 카메라 제어를 시도할 때 즉시 멈춘다는 점입니다. 애니메이션을 더 편리하게 시작하기 위해 `AnimateCamera` function에 단축키를 할당할 수 있습니다:
+
+![](/doc-img/en-blueprint-camera-11.png)
+
+## 다른 카메라 파라미터 애니메이션
+
+카메라 위치와 방향뿐만 아니라 **Field of View**(시야각) 같은 다른 카메라 파라미터도 애니메이션할 수 있어요. 예를 들어, 아래 blueprint는 카메라가 움직이기 시작할 때 줌 애니메이션을 시작합니다:
+
+![](/doc-img/en-blueprint-camera-12.png)
+
+다른 카메라 노드도 자유롭게 실험해 보세요!
+
+## Free Look 카메라 애니메이션
+
+지금까지는 Orbit Character 제어 모드에서 카메라를 애니메이션했지만 모든 영화 같은 카메라 움직임이 캐릭터 주변을 도는 것은 아니에요. 또는 캐릭터의 위치와 상관없이 고정된 두 위치 사이에서 카메라를 이동시키고 싶을 수 있어요. 이 경우 **Set Asset Transform** 노드를 사용할 수 있습니다:
+
+![](/doc-img/en-blueprint-camera-15.png)
+
+이 노드는 Camera Orbit Character 노드와 동일하게 작동하지만, Free Look 제어 모드에서 카메라의 파라미터를 기록하고 재생해요. 대상 위치와 방향을 기록하려면 **Align Target With Asset** 버튼을 사용하세요.
+
+:::tip
+이 노드의 이름이 Set Asset Transform인 이유는 이 노드를 사용하여 소품이나 캐릭터와 같은 다른 에셋도 이동하고 애니메이션할 수 있다는 점입니다!
+:::
+
+## 카메라 전환하기
+
+**Switch Main Camera** 노드가 있다는 사실을 언급했었나요? 이 노드는 두 카메라 간을 전환하며, 페이드 전환 옵션도 제공해요. 예를 들어, 아래 blueprint는 Ctrl+1과 Ctrl+2를 누르면 두 카메라를 전환합니다:
+
+![](/doc-img/en-blueprint-camera-14.png)
+
+결과는 이렇게 나옵니다:
+
+
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/contact.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/contact.md
new file mode 100644
index 0000000..80ff1c7
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/contact.md
@@ -0,0 +1,93 @@
+---
+sidebar_position: 130
+---
+
+# 스파크 만들기
+
+이제 스트림을 더욱 흥미롭게 만들어볼 시간이에요! 이 튜토리얼에서는 Warudo의 강력한 접촉 시스템을 소개할 거예요. 이 시스템을 사용하면 물체가 다른 물체와 접촉할 때 다양한 재미있는 효과를 트리거할 수 있어요.
+
+
+
접촉 시스템을 사용해 손가락 사이에 불꽃을 일으켜 카타나를 꺼내는 모습.
+
+:::info
+이 튜토리얼을 진행하려면 손 추적이나 전신 추적이 필요해요. 손 추적의 경우 [MediaPipe](../../mocap/mediapipe)도 괜찮지만, 더 나은 정확도를 위해 [Leap Motion](../../mocap/leap-motion)을 사용하는 것을 추천해요.
+:::
+
+## 접촉 시스템
+
+접촉 시스템은 개념이 간단해요: 두 개의 물체가 접촉할 때 플로우를 트리거할 수 있어요. 예를 들어, 두 검지 손가락의 끝이 접촉할 때 스파크를 생성하는 것이에요. 이제 어떻게 구현할 수 있는지 살펴보죠!
+
+먼저, **On Contact** 노드를 살펴볼게요. 믿기지 않겠지만, 이 노드 하나만으로 접촉 시스템 전체를 구현할 수 있어요! 본질적으로 이 노드는 **Contact Senders**에 있는 물체가 **Contact Receivers**에 있는 물체와 접촉하는지 자동으로 확인하고 **On Contact Enter** (물체와 접촉하는 순간), **On Contact Stay** (물체와 접촉하는 동안) 그리고 **On Contact Exit** (물체와 더 이상 접촉하지 않는 순간) 플로우 아웃풋을 각각 트리거해요.
+
+![](/doc-img/en-blueprint-contact-2.png)
+
+먼저 **Contact Senders**에 항목을 추가하고 아래와 같이 설정해요:
+
+![](/doc-img/en-blueprint-contact-4.png)
+
+이제 오른손을 들어 올리면, 검지 손끝에 빨간색 구가 나타나는 것을 볼 수 있을 거예요:
+
+![](/doc-img/en-blueprint-contact-3.png)
+
+그다음 **Contact Receivers**에 항목을 추가하고 아래와 같이 설정해요:
+
+![](/doc-img/en-blueprint-contact-5.png)
+
+이제 손을 들어 올리면 왼손 검지 끝에 녹색 구가 나타날 거예요:
+
+![](/doc-img/en-blueprint-contact-6.png)
+
+이제 실제로 전기 스파크를 생성하는 작업만 남았어요! 다행히도 **Spawn Particle** 노드를 사용하면 쉽게 할 수 있어요. 이 노드를 편집기에 추가하고 **Source** 옵션에서 좋아하는 파티클(저는 "Basic Impact 3"을 사용했어요)을 선택하고 적절한 **Scale**을 설정해요(저는 0.1을 사용했어요). 그런 다음 **On Contact → On Contact Stay**를 **Spawn Particle → Enter**에, 그리고 **On Contact → Contact Position**을 **Spawn Particle → Position Offset**에 아래와 같이 연결해 주세요:
+
+![](/doc-img/en-blueprint-contact-7.png)
+
+이제 손을 맞대면 손끝에서 불꽃이 튀는 것을 볼 수 있을 거예요! 생각보다 쉽죠?
+
+![](/doc-img/en-blueprint-contact-1.png)
+
+:::tip
+설정을 완료한 후에는 **Visualized** 옵션을 No로 설정해 빨간색과 녹색 구를 숨길 수 있어요.
+:::
+
+## 쓰로틀 플로우
+
+파티클이 매우 빠르게 생성되어 스파크 효과가 지나치게 밀집된 것을 알아챘을 거예요. 이는 **On Contact Stay** 플로우 아웃풋이 물체들이 접촉 중인 매 프레임마다 트리거되기 때문이에요. 만약 Warudo를 60 FPS로 실행 중이라면, 매 초당 60개의 파티클이 생성되는 거죠!
+
+이 플로우 속도를 줄이기 위해 **Throttle Flow** 노드를 사용할 수 있어요. 쓰로틀은 물의 흐름을 조절하는 밸브와 같고 Throttle Flow 노드는 플로우 아웃풋의 속도를 조절해요. 아래 예시에서는 **Interval**을 0.1초로 설정한 Throttle Flow 노드가 0.1초마다 최대 한 번씩만 플로우 아웃풋을 트리거해요:
+
+![](/doc-img/en-blueprint-contact-8.png)
+
+플로우 속도를 원하는 대로 조정할 수 있어요. 예를 들어, 초당 20개의 불꽃을 생성하고 싶다면 Interval을 0.05로 설정하면 돼요.
+
+## 카타나 뽑기
+
+이번엔 다른 예시로 등 뒤에서 카타나를 뽑는 동작을 만들어 볼게요! 카타나 소품(혹은 칼, 단검, 원하는 무기)을 추가하고 캐릭터의 왼손에 아래처럼 부착해 주세요:
+
+![](/doc-img/en-blueprint-contact-9.png)
+
+먼저 카타나를 숨기기 위해 **Prop → Enabled**을 No로 설정하세요. 이제 접촉 시스템을 설정할 차례입니다. 새 On Contact 노드를 추가한 뒤, Contact Senders 목록에 아래와 같은 설정을 추가하세요:
+
+![](/doc-img/en-blueprint-contact-12.png)
+
+그리고 Contact Receivers 목록에 다음 설정을 추가하세요:
+
+![](/doc-img/en-blueprint-contact-13.png)
+
+오른쪽 어깨에 빨간색 구체, 왼손에는 초록색 구체가 보여야 해요. **Position Offset** 옵션을 조정해 손이 어깨 쪽에 닿도록 위치를 편안하게 맞춰주세요.
+
+![](/doc-img/en-blueprint-contact-11.png)
+
+마지막으로, 손이 어깨에 닿을 때마다 소품을 활성화하는 **Toggle Asset Enabled** 노드를 추가하면 됩니다:
+
+![](/doc-img/en-blueprint-contact-10.png)
+
+이제 왼손을 오른쪽 어깨에 대면 손에 카타나가 나타나는 것을 볼 수 있을 거예요! 카타나가 나타날 때 **Play Sound** 노드를 추가해 소리 효과를 넣거나, 파티클을 추가하거나 원하는 대로 설정할 수 있어요. 이것이 접촉 시스템의 매력입니다!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/dance.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/dance.md
new file mode 100644
index 0000000..1fe14ae
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/dance.md
@@ -0,0 +1,127 @@
+---
+sidebar_position: 50
+---
+
+# 댄스 댄스 댄스
+
+시청자들에게 감사의 인사를 전할 재미있는 방법을 찾고 있나요? 이 튜토리얼에서는 YouTube Super Chat이나 Twitch 포인트 보상을 받을 때 캐릭터가 춤을 추도록 만드는 방법을 배울 거예요.
+
+
+
시청자들의 응원에 감사하며 춤을 추는 모습!
+
+## 스트리밍 플랫폼 연결하기
+
+시작하기 전에, Warudo를 Twitch, YouTube 또는 Bilibili 계정에 연결해야 해요. 이를 위해 Onboarding Assistant를 사용하세요. 아직 연결하지 않았다면 [시작하기](../../tutorials/readme-1#interaction-setup) 튜토리얼을 참조하세요. 이 튜토리얼에서는 Twitch에서 스트리밍한다고 가정하지만 다른 플랫폼에서도 비슷한 절차로 진행할 수 있어요.
+
+:::tip
+[Streamer.bot](Streamer.bot)과 같은 서드파티 통합을 사용하여, 도네이션이나 다른 스트림 이벤트를 받을 때 웹소켓 메시지를 Warudo로 보낼 수 있어요. 이 경우 Onboarding Assistant를 사용할 필요가 없어요.
+:::
+
+간단한 예제부터 시작해봅시다: Twitch 포인트 보상을 받을 때 캐릭터가 춤을 추게 만들기 위해서는 두 개의 노드만 필요해요:
+
+![](/doc-img/en-blueprint-dance-1.png)
+
+:::info
+**Play Character One Shot Overlay Animation** 노드는 캐릭터의 일회성 애니메이션을 재생해요. 즉, 캐릭터의 기본 애니메이션을 변경하는 대신, 현재 재생 중인 기본 애니메이션 위에 애니메이션이 재생됩니다. 애니메이션이 끝나면 캐릭터는 자동으로 기본 애니메이션으로 돌아가요.
+:::
+
+:::tip
+**On Twitch Channel Points Redeemed** 노드 대신, 원하는 이벤트 노드를 사용할 수도 있어요. 예를 들어, YouTube에서 스트리밍하는 경우 **On YouTube Super Chat Received** 노드를 사용할 수 있어요. 또는 웹소켓을 사용해 통신할 수 있는 서드파티 통합을 사용하는 경우 **On WebSocket Message Received** 노드를 사용할 수 있어요.
+:::
+
+테스트는 어떻게 할까요? 물론, 누군가가 Twitch 포인트 보상을 사용할 때까지 기다릴 수도 있지만, 그 방법은 비효율적이에요. 대신, Warudo에서는 노드를 수동으로 트리거할 수 있어요. Play Character One Shot Overlay Animation 노드의 **Enter** 플로우 인풋을 클릭해보세요:
+
+![](/doc-img/en-blueprint-dance-2.png)
+
+이제 캐릭터가 춤을 추기 시작합니다!
+
+## If Branch
+
+현재는 어떤 Twitch 포인트 보상을 받아도 캐릭터가 춤을 추기 시작해요. 하지만 특정 보상, 예를 들어 "댄스"라는 이름의 보상을 받을 때만 춤추게 하려면 어떻게 할까요? 여기서 **If Branch** 노드를 소개할게요. 두 노드 사이에 If Branch 노드를 추가해 볼게요:
+
+![](/doc-img/en-blueprint-dance-3.png)
+
+이전에 봤던 Flip Flop 노드처럼, If Branch 노드도 두 개의 플로우 아웃풋인 **If True**와 **If False**를 가지고 있어요. 그러나 번갈아 트리거되는 것이 아니라, **Condition** 데이터 인풋 값에 따라 어느 플로우 아웃풋이 트리거될지가 결정됩니다. Condition이 Yes라면 If True 플로우 아웃풋이 트리거되고, 그렇지 않으면 If False 플로우 아웃풋이 트리거됩니다.
+
+이것을 테스트하기 위해 If Branch 노드의 Enter 플로우 인풋을 클릭해보세요. 현재 Condition이 No로 설정되어 있기 때문에 If False 플로우 아웃풋이 트리거되지만, 이후 연결된 것이 없으므로 아무 일도 일어나지 않아요.
+
+Condition을 Yes로 설정하고 다시 Enter 플로우 인풋을 클릭해보세요. 이번에는 If True 플로우 아웃풋이 트리거되어 캐릭터가 춤을 추기 시작해요!
+
+이제 남은 것은 Condition을 의미 있는 값으로 설정하는 것이에요. 이 경우 우리는 Twitch 포인트 보상 이름이 "댄스"와 동일한지 확인하고 싶어요. 이를 위해, 정확히 그 기능을 하는 **String Equal** 노드를 추가하고 **String Equal → Output Boolean** 데이터 아웃풋을 **If Branch → Condition**에 연결해보세요:
+
+![](/doc-img/en-blueprint-dance-4.png)
+
+String Equal 노드는 매우 간단해요: 두 문자열을 비교하여 같으면 Yes를 출력하고, 다르면 No를 출력해요. (문자열은 "텍스트"를 나타내는 멋진 방법이에요.) 이 경우, **On Twitch Channel Points Redeemed → Reward Title**을 **String Equal → A**에 연결하고 **String Equal → B**를 "댄스"로 설정해요. 그럼 String Equal 노드가 트리거되면, Twitch 포인트 보상 이름이 "댄스"와 같은지 확인하고 그 결과를 If Branch 노드로 전달해요. (참고로 boolean은 "yes 또는 no를 나타내는 멋진 방벙이에요.")
+
+:::tip
+보상 이름을 B에 연결하고, A를 "댄스"로 설정해도 됩니다. 이 경우, 순서는 중요하지 않아요.
+:::
+
+![](/doc-img/en-blueprint-dance-5.png)
+
+이제 완성됐어요! Twitch 채널에서 "댄스" 보상을 사용해 보세요. 그러면 캐릭터가 춤을 추기 시작할 거예요!
+
+## 댄스 랜덤화
+
+항상 같은 춤 애니메이션을 재생하는 대신 랜덤으로 춤 애니메이션을 선택하고 싶다면 어떻게 할까요? [이전 튜토리얼](buried)에서 Get Random Prop 노드를 기억한다면 **Get Random Character Animation** 노드가 있다는 것도 놀랍지 않을 거예요. 이를 이용해 아래와 같이 blueprint를 설정할 수 있어요:
+
+![](/doc-img/en-blueprint-dance-6.png)
+
+Play One Shot Overlay Animation 노드가 트리거되면, Get Random Character Animation 노드도 트리거되어 **Character Animations** 목록에서 랜덤 애니메이션을 출력해요. 제 경우에는 "Short Dance 1", "Short Dance 2", "Short Dance 3" 중 하나가 재생되도록 설정했어요.
+
+## 결과 랜덤화
+
+VTuber로서 도네이션을 받을 때 예상치 못한 재미 요소를 추가하고 싶을 수 있어요. 단순히 춤을 시작하는 대신, [공중으로 발사](ragdoll) 또는 [인형 더미를 소환](buried)하는 등의 다른 일이 발생할 가능성을 넣는 거죠. 이를 구현해 봅시다!
+
+:::tip
+아래 예시에서는 간단히 하기 위해 **Get Random Character Animation** 노드를 제거했지만, 원한다면 그대로 유지할 수 있어요.
+:::
+
+먼저, If Branch ↔ Play Character One Shot Overlay Animation 사이의 연결을 제거하세요. 그 다음, If Branch 노드 바로 뒤에 **Switch On Integer** 노드를 추가하고, **Cases** 리스트에 3개의 항목을 아래와 같이 추가하세요:
+
+![](/doc-img/en-blueprint-dance-7.png)
+
+Switch On Integer 노드는 다음과 같이 작동해요: 노드가 트리거되면 **Input Integer** 데이터 인풋의 값을 확인한 후, 해당하는 플로우 아웃풋을 트리거해요. 예를 들어, Input Integer가 1이면 1로 라벨된 플로우 아웃풋이 트리거되고, 2면 2로 라벨된 플로우 아웃풋이, 3이면 3로 라벨된 플로우 아웃풋이 트리거됩니다.
+
+각 플로우 아웃풋을 다른 결과에 연결해 봅시다. 예를 들어, 저는 1로 라벨된 플로우 아웃풋을 Play Character One Shot Overlay Animation 노드에, 2번을 **Play Sounds** 노드에, 3번을 **Spawn Particle** 노드에 연결했어요. 두 노드의 **Source** 옵션을 설정하는 것을 잊지 마세요:
+
+![](/doc-img/en-blueprint-dance-8.png)
+
+이제 테스트해 봅시다! **Switch On Integer → Input Integer** 를 1에서 3 사이의 숫자로 설정하고 Enter 플로우 인풋을 클릭하세요. 모든 것이 잘 설정되었다면, 해당하는 노드가 트리거되는 것을 볼 수 있을 거예요.
+
+![](/doc-img/en-blueprint-dance-9.png)
+
+마지막으로 해야 할 일은 Input Integer를 랜덤화하는 거예요. 이미 예상하셨겠지만, **Generate Random Integer** 노드가 있어요! **Value Min**을 1로, **Value Max**를 3으로 설정해 1에서 3 사이의 정수를 출력하게 해요.
+
+![](/doc-img/en-blueprint-dance-10.png)
+
+아래는 최종 blueprint입니다. 이제 시청자들과 함께 재미있게 즐겨보세요!
+
+![](/doc-img/en-blueprint-dance-11.png)
+
+## If Branch에 대해 더 알아보기
+
+우리는 If Branch 조건으로 String Equal 노드를 사용했지만, 다른 조건들도 사용할 수 있어요. 예를 들어, **Integer Greater Than Or Equal** 노드를 사용해 **On Twitch Channel Points Redeemed → Reward Cost** 가 1,000 이상인지를 확인할 수 있어요. 이 설정을 통해 1,000 포인트 이상이 걸린 Twitch 포인트 보상을 받을 때 캐릭터가 춤을 추게 할 수 있죠.
+
+또한 **Boolean AND**와 **Boolean OR** 노드를 사용해 여러 조건을 결합할 수 있어요. 아래 blueprint는 Twitch 포인트 보상 이름이 "DJ"와 동일하고, 보상을 사용한 사용자의 이름에 "bot"이 포함된 경우를 확인해요:
+
+![](/doc-img/en-blueprint-dance-12.png)
+
+다음 blueprint는 보상이 500 포인트 이상이거나, 보상을 사용한 사용자가 "hakuyatira"일 때 캐릭터가 춤을 추게 합니다::
+
+![](/doc-img/en-blueprint-dance-13.png)
+
+## 상호작용 Blueprints
+
+만약 Onboarding Assistant를 사용해 상호작용 blueprint를 설정해봤다면, 방금 우리가 만든 blueprint와 매우 유사하다는 것을 알았을 거예요! 더 많은 노드가 포함될 수 있지만, 핵심 로직은 같아요: 스트림 이벤트를 받으면 흥미로운 무언가를 실행하는 것이죠.
+
+이제 새로 배운 지식을 바탕으로 자신만의 상호작용 blueprints를 만들 수 있어요! 이전 튜토리얼에서 배운 내용을 활용해 자신만의 독특한 상호작용을 채널에 추가해 보세요. 더 재미있는 요소를 찾고 있다면 계속 읽어보세요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/karaoke.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/karaoke.md
new file mode 100644
index 0000000..49f459a
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/karaoke.md
@@ -0,0 +1,111 @@
+---
+sidebar_position: 40
+---
+
+# 노래방 타임
+
+누구나 스트림에서 노래방을 즐기잖아요? 이제 우리는 3D 세계에 있으니, 캐릭터가 마이크를 들고 노래하는 현실감을 더해봅시다. 이 튜토리얼에서는 단축키를 사용해 노래하는 포즈와 마이크 소품을 전환하는 방법을 보여줄 거예요.
+
+
+
열정적으로 노래하는 모습!
+
+:::tip
+이 노래방 방 환경을 사용하고 싶다면, Warudo의 **Discover** 탭에서 Steam Workshop을 통해 다운로드할 수 있어요.
+:::
+
+## 기본 애니메이션 재생하기
+
+먼저, 마이크 소품을 추가하고 캐릭터의 왼손에 부착하세요. 어떻게 하는지 잘 모르겠다면 [시작하기](../../tutorials/readme-1.md#assets-tab) 튜토리얼을 참조하세요. 그런 다음, 소품 에셋에서 **Enabled**를 No로 설정하여 마이크를 일단 숨깁니다.
+
+이제 우리가 익숙한 On Keystroke Pressed 노드를 사용하여 아래의 blueprint를 재현해보세요. 여기에는 두 개의 새로운 노드가 추가돼요: **Toggle Asset Enabled**와 **Play Character Idle Animation**. (이름만 봐도 기능을 알 수 있죠?) **Toggle Asset Enabled → Asset**에는 마이크 소품을 설정하고, **Play Character Idle Animation → Animation**에는 노래하는 포즈를 설정하세요. 저는 "010_0970"을 사용했지만, 다른 포즈를 실험해 봐도 좋아요.
+
+![](/doc-img/en-blueprint-karaoke-1.png)
+
+맞아요! 이제 M 단축키를 누르면 마이크가 나타나고, 캐릭터가 마이크를 들어 올려 노래할 준비를 해요.
+
+
+
+이건 너무 쉬우니, 더 재미있게 만들어봅시다. 마이크가 나타날 때 "우우웅" 소리 효과를 재생하는 방법을 생각해 볼 수 있을까요?
+
+"이건 쉽지!"라고 하면서, 검색창에 "sound"를 입력하죠. 그리고 바로 **Play Sound** 노드가 보이네요. Play Sound 노드를 노드 에디터에 추가하고 **Sound Source** 를 "우우웅" 소리 효과로 설정한 다음, Play Character Idle Animation 노드 옆에 연결합니다:
+
+![](/doc-img/en-blueprint-karaoke-2.png)
+
+M을 누르면 소리가 잘 재생됩니다. 훌륭해요! 하지만 소리가 마이크를 꺼낼 때나 넣을 때 모두 재생된다는 점을 눈치챘나요? 마이크를 꺼낼 때만 소리가 나게 할 방법이 있을까요?
+
+## 플립 플롭
+
+정답은 **Flip Flop** 노드입니다. 보통 노드에는 Enter 플로우 인풋과 Exit 플로우 아웃풋이 있지만, Flip Flop 노드는 특별해요: **A**와 **B**두 개의 Exit 플로우 아웃풋을 가지고 있어요. Flip Flop 노드가 처음 트리거될 때는 **A** 플로우가 실행되고, 두 번째로 트리거되면 **B** 플로우가 실행돼요. 세 번째로는 다시 **A** 플로우, 그다음은 **B** 플로우 이렇게 번갈아가며 동작해요.
+
+정말 편리하죠? 이제 우리의 blueprint를 아래처럼 재구성해봅시다!
+
+![](/doc-img/en-blueprint-karaoke-3.png)
+
+이제 마이크를 꺼낼 때만 소리 효과가 재생될 거예요!
+
+## 애니메이션 프로필
+
+이 방식은 귀엽지만, Play Character Idle Animation 노드를 사용할 때의 한계가 있어요. 미리 정의된 포즈/애니메이션만 선택할 수 있다는 점이죠. 예를 들어, 저는 캐릭터가 왼손만 들어 올리게 하고 싶지만, 그런 포즈는 목록에 없어요. 이때 유용한 것이 **Character → Overlaying Animations**입니다.
+
+캐릭터 에셋을 열고, **Idle Animation**을 Generic으로 설정해 기본 포즈로 초기화한 후, **Overlaying Animations**까지 스크롤하세요. **+** 버튼을 클릭해 새 항목을 추가하고, **Animation** 드롭다운에서 "010_0970"을 선택하세요.
+
+![](/doc-img/en-blueprint-karaoke-8.png)
+
+캐릭터가 예상대로 포즈를 취해요:
+
+![](/doc-img/en-blueprint-karaoke-9.png)
+
+이제 왼손만 들어 올리도록 하려면, **Masked**를 Yes로 설정하고 **Masked Body Parts** 목록에 **Left Arm**을 추가해 아래와 같이 설정해요:
+
+![](/doc-img/en-blueprint-karaoke-4.png)
+
+또한, **Weight** 옵션을 조정해 원하는 포즈를 얻을 때까지 설정하세요. 이 경우, 0.88로 설정했지만, 캐릭터마다 다를 수 있어요.
+
+이제 캐릭터는 왼손만 들어 올리게 돼요:
+
+![](/doc-img/en-blueprint-karaoke-5.png)
+
+그렇다면 이 포즈를 blueprint에서 어떻게 트리거할 수 있을까요? 먼저 애니메이션 프로필을 저장해야 해요. 애니메이션 프로필은 캐릭터의 현재 애니메이션 설정(Idle Animation, Overlaying Animations, Override Hand Poses 등)을 저장한 스냅샷이에요. **Save Animation Profile** 버튼을 클릭해봅시다:
+
+![](/doc-img/en-blueprint-karaoke-6.png)
+
+이제 이름을 붙여주세요, 예를 들어 LeftHandMic라고 하고, **OK**를 클릭하세요:
+
+![](/doc-img/en-blueprint-karaoke-7.png)
+
+애니메이션 프로필을 저장했으니, 이제 **Load Animation Profile** 버튼을 사용해 언제든지 이를 불러올 수 있어요!
+
+곧 blueprint에서 이 작업을 어떻게 수행할 수 있는지 살펴보겠지만, 먼저 기본 포즈를 위한 또 다른 애니메이션 프로필을 만들어 봅시다. 저는 간단하게 처리했어요: Overlaying Animations 목록을 비우고, Idle Animation을 Generic으로 설정했어요. 물론, 여러분은 커스텀 기본 포즈를 만들어도 좋아요!
+
+"Idle"이라는 이름으로 또 다른 애니메이션 프로필을 저장한 후, 이제 준비가 끝났어요! blueprint로 가서, Play Character Idle Animation 노드를 Load Animation Profile 노드로 교체하세요. 그리고 **Profile** 드롭다운에서 올바른 프로파일을 선택하는 것을 잊지 마세요!:
+
+![](/doc-img/en-blueprint-karaoke-10.png)
+
+이제 M을 누르면 "LeftHandMic" 애니메이션 프로필이 로드되고, 다시 M을 누르면 "Idle" 애니메이션 프로필이 로드됩니다!
+
+:::tip
+위는 Overlaying Animations와 애니메이션 프로파일을 사용하는 간단한 예시일 뿐이에요. 중요한 점은 이 기능들을 통해 다양한 포즈와 애니메이션을 섞어서 사용할 수 있어 원하는 대로 캐릭터를 포즈할 수 있다는 거예요.
+:::
+
+## 플로우 지연하기
+
+마지막으로 작은 문제를 해결해봅시다. 캐릭터가 마이크를 넣을 때, 마이크가 즉시 사라지는 것을 눈치챘을 거예요. 캐릭터가 마이크를 다 넣은 후에 사라지게 한다면 더 현실적일 거예요. 어떻게 할 수 있을까요?
+
+:::tip
+힌트: **Delay Control Flow** 노드를 사용하면 가능합니다. 직접 시도해보고 계속 읽어보세요!
+:::
+
+여기 간단한 해결책이 있어요: Toggle Asset Enabled와 Play Character Idle Animation 노드의 순서를 바꾸고, 그 사이에 Delay Control Flow 노드를 추가하는 거예요. **Delay**를 1초로 설정하여 캐릭터가 마이크를 넣은 후 1초 후에 마이크가 사라지도록 합니다.
+
+![](/doc-img/en-blueprint-karaoke-11.png)
+
+Delay Control Flow 노드는 지정된 지연 시간 후에 Exit 플로우를 트리거해요. 이 노드를 사용하면 연속적인 이벤트를 만들 수 있어요. 예를 들어, 캐릭터가 5초 동안 춤을 추다가 머리 위에 여러 소품을 떨어뜨리고 마지막에 폭발 파티클 효과를 재생하는 등의 일련의 이벤트를 만들 수 있어요. 이 모든 것은 당신의 상상력에 달렸습니다!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/nap.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/nap.md
new file mode 100644
index 0000000..03dcbde
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/nap.md
@@ -0,0 +1,118 @@
+---
+sidebar_position: 100
+---
+
+# 낮잠 자기
+
+스트리밍 도중 잠깐 쉬는 건 흔한 일이에요. Warudo에서는 얼굴 추적이 중단되면 캐릭터가 서서히 기본 포즈와 표정으로 전환돼요. 이 튜토리얼에서는 기본 포즈 대신 캐릭터가 자연스럽게 잠자는 포즈와 표정으로 전환되도록 만들어, 캐릭터에 약간의 현실감을 더할 거예요.
+
+
+
일하다가 잠들었어요!
+
+:::info
+이 튜토리얼은 iFacialMocap이나 MediaPipe를 사용해 얼굴을 추적하는 상황을 전제로 해요. 다른 얼굴 추적 시스템은 얼굴 추적이 중단되었을 때 신호를 제공하지 않아 작동하지 않을 수 있어요.
+:::
+
+## 얼굴 추적 Blueprint
+
+얼굴 추적 blueprint를 열어봐요! 온보딩 과정에서 선택한 옵션에 따라 당신의 blueprint가 아래와 다르게 보일 수 있지만, 기본 아이디어는 같아요. 그리고 그만큼 복잡해 보일 수 있어요—노드와 연결선이 엄청 많으니까요!
+
+![](/doc-img/en-blueprint-nap-1.png)
+
+모든 노드를 하나하나 살펴보지는 않겠지만, 몇 가지 부분으로 나누어 설명해 볼게요. 먼저, 왼쪽에 있는 이 노드 시퀀스를 볼 수 있을 거예요:
+
+![](/doc-img/en-blueprint-nap-2.png)
+
+이건 쉽죠! [이전 튜토리얼](balloon#resetting-bone-scale)에서 On Disable Blueprint 노드를 이미 봤을 거예요. 이 노드는 blueprint가 비활성화될 때 트리거돼요. 그리고 이 얼굴 추적 blueprint는 **Character → Motion Capture**를 끌 때 비활성화돼요. 그래서 모션 캡처를 끄면 **Reset Character Tracking BlendShapes** 노드로 캐릭터의 얼굴 표정이 리셋되고 **Reset Overridden Character Bones** 노드로 캐릭터의 뼈대(예: 머리, 눈)가 리셋돼요.
+
+이제 blueprint 오른쪽을 살펴볼까요:
+
+![](/doc-img/en-blueprint-nap-3.png)
+
+[On Update node](balloon#on-update)노드도 이미 익숙할 거예요. 이 노드는 Warudo가 실행되는 매 프레임마다 트리거돼요. 이 노드 시퀀스는 이렇게 말하는 거예요: 매 프레임마다 캐릭터의 블렌드셰이프 (**Set Character Tracking BlendShapes**), 뼈대 (**Override Character Bone Rotation Offsets**), 그리고 루트 위치 (**Override Character Root Position**)를 업데이트해요. 그런데 어떤 블렌드셰이프와 뼈대, 그리고 루트 위치를 업데이트하는 걸까요? 그건 blueprint 중간에 있는 노드들의 역할이에요.
+
+![](/doc-img/en-blueprint-nap-16.png)
+
+## 스위치 블렌드셰이프 리스트
+
+다행히도, 이 튜토리얼에서는 몇 가지 노드만 살펴보면 돼요. **Get iFacialMocap Receiver Data** 노드(또는 MediaPipe를 사용하는 경우 **Get MediaPipe Receiver Data** 노드)를 찾아보세요; 이 노드는 얼굴 추적 데이터를 제공하는 "source" 노드예요.
+
+![](/doc-img/en-blueprint-nap-4.png)
+
+현재 노드 연결이 너무 가까이 있어서, 더 명확하게 보이도록 Get iFacialMocap Receiver Data 노드를 아래쪽 왼쪽으로 이동시켜 볼게요:
+
+![](/doc-img/en-blueprint-nap-5.png)
+
+이제 blueprint를 확대하고 이동해서 다음 3개의 노드를 찾아보세요: Get iFacialMocap Receiver Data, Empty BlendShape List, 그리고 Switch BlendShape List. 아직 **Switch BlendShape List** 노드에 대해 잘 모르지만, 연결 상태만 보고 이 노드가 어떤 역할을 하는지 추측할 수 있나요?
+
+![](/doc-img/en-blueprint-nap-6.png)
+
+이제 하나씩 확인해보죠. 먼저 **Get iFacialMocap Receiver Data → Is Tracked**가 **Switch BlendShape List → Condition**에 연결되어 있는 것을 볼 수 있어요. 이 연결은 Switch BlendShape List 노드가 얼굴이 추적되는지 여부를 신경 쓴다는 걸 의미해요. 얼굴이 추적되면 Condition은 Yes이고, 그렇지 않으면 No가 됩니다.
+
+다음으로 **Empty BlendShape List → Output**이 **Switch BlendShape List → If False**에, **Get iFacialMocap Receiver Data → BlendShapes**가 **Switch BlendShape List → If True**에 연결되어 있어요. 흠, 이제 점점 감이 오네요!
+
+실제로, Switch BlendShape List 노드는 현재 얼굴이 추적되고 있는지 확인해요. 만약 얼굴이 추적되고 있다면 추적된 blendshapes를 아웃풋으로 넘겨서 캐릭터의 표정을 반영하고, 그렇지 않으면 빈 blendshape list를 아웃풋으로 넘겨서 캐릭터의 표정을 없애요.
+
+이 노드를 더 강력하게 만드는 점은 두 blendshape list 사이를 부드럽게 전환할 수 있다는 점이에요. **To True/False Transition Time**, **To True/False Transition Delay** 그리고 **To True/False Transition Easing** 옵션을 통해 얼굴이 추적되거나 추적되지 않을 때 캐릭터의 표정 변화를 정밀하게 제어할 수 있어요. 예를 들어, To False Transition Time을 0.5초로 설정하고 To False Transition Delay를 1초로 설정하면, 얼굴이 더 이상 추적되지 않고 1초 후에 캐릭터의 표정이 0.5초 동안 부드럽게 빈 blendshape list로 전환돼요(즉, 자연스러운 표정이 되는 거죠).
+
+얼굴이 더 이상 추적되지 않을 때 캐릭터의 표정을 변경하려면, 현재 빈 blendshape list가 입력된 Switch BlendShape List → If False인풋을 수정하면 돼요. **BlendShape List Set BlendShape** 노드를 그 사이에 추가해서 리스트에 blendshape을 더할 수 있어요.
+
+![](/doc-img/en-blueprint-nap-7.png)
+
+위의 예시에서는 하나의 엔트리만 있는 blendshape list를 만들고 있어요: `Blink` 를 1로 설정한 거죠. 이 blendshape list는 얼굴 추적이 중단될 때 아웃풋으로 전달되어, 캐릭터가 눈을 감게 돼요.
+
+:::tip
+만약 캐릭터에 `Blink` 블렌드셰이프가 없다면, 캐릭터와 함께 제공되는 `eyeBlinkLeft`와 `eyeBlinkRight` 같은 blink 블렌드셰이프를 사용할 수 있어요. 여러 개의 블렌드셰이프를 설정하려면, 간단하게 BlendShape List Set BlendShape 노드를 추가로 연결해 주면 돼요.
+:::
+
+이제 iPhone 전면 카메라 앞에 손을 대어 얼굴 추적을 차단해보세요. (MediaPipe를 사용하는 경우, 얼굴을 카메라에서 멀리 이동시키면 돼요.) 그러면 캐릭터가 눈을 감는 것을 확인할 수 있을 거예요!
+
+![](/doc-img/en-blueprint-nap-13.png)
+
+기본 설정에서는 눈을 빠르게 감게 되므로, 좀 더 자연스러운 전환을 위해 **To False Transition Time**과 **To False Transition Delay** 옵션을 조정하는 것이 좋아요. 저는 각각 2초와 1초로 설정했어요:
+
+![](/doc-img/en-blueprint-nap-8.png)
+
+## 스위치 로테이션 리스트
+
+:::info
+이 섹션을 읽기 전에, 온보딩 과정에서 기본적으로 활성화된 기본 머리 애니메이션(기본 상태에서 트래킹하지 않을 때 활성화됨)이 활성화되어 있다면, 먼저 비활성화해야 해요. **Generate Idle Head Animation** 노드를 찾아 **Enabled**를 No로 설정하세요:
+
+![](/doc-img/en-blueprint-nap-12.png)
+:::
+
+
+캐릭터가 고개를 옆으로 기울이는 잠자는 포즈로 전환하려면, 우리가 방금 했던 것과 매우 비슷한 과정을 따르게 돼요. 이번에도 Get iFacialMocap Receiver Data 노드를 아래쪽으로 이동시켜 보세요:
+
+![](/doc-img/en-blueprint-nap-9.png)
+
+오른쪽에 있는 **Default Character Rotation List**와 **Switch Rotation List** 노드가 보이도록 해요:
+
+![](/doc-img/en-blueprint-nap-10.png)
+![](/doc-img/en-blueprint-nap-11.png)
+
+3D 캐릭터의 포즈는 각 뼈가 얼마나 회전하는지에 따라 정의돼요. 그래서 여기서는 블렌드셰이프 대신 "뼈 회전"을 많이 보게 되는 거예요. Switch Rotation List 노드는 얼굴이 추적되고 있는지 확인하고, 추적 중이면 뼈 회전 데이터를 아웃풋으로 전달하고, 그렇지 않으면 기본 회전 리스트를 아웃풋으로 전달해요.
+
+고개를 옆으로 기울이려면, **Offset Character Bone Rotation List** 노드를 추가해 머리 회전을 조정할 수 있어요:
+
+![](/doc-img/en-blueprint-nap-15.png)
+
+저는 **Head**를 **Character Bones** 리스트에 추가하고 **Rotation Offset** 을 (15, 0, -20)으로 설정했어요. 이는 머리가 앞쪽으로 15도, 오른쪽으로 20도 회전된다는 의미예요. 원하는 대로 회전을 조정하거나, 더 많은 뼈를 리스트에 추가해 캐릭터의 잠자는 포즈를 더욱 복잡하게 만들 수도 있어요!
+
+:::tip
+숫자를 직접 입력하는 대신, X/Y/Z 레이블을 왼쪽 또는 오른쪽으로 드래그해 숫자를 조정할 수도 있어요. 이 트릭은 모든 숫자 입력에 대해 작동해요!
+:::
+
+이제 얼굴 추적이 중단되면, 캐릭터가 고개를 옆으로 기울이게 될 거예요!
+
+![](/doc-img/en-blueprint-nap-14.png)
+
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/spinning.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/spinning.md
new file mode 100644
index 0000000..aeba284
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/spinning.md
@@ -0,0 +1,129 @@
+---
+sidebar_position: 110
+---
+
+# 의자 돌리기
+
+의자에서 아이처럼 빙글빙글 돌고 싶었던 적이 있나요? 이제 그걸 할 수 있어요! 이 튜토리얼에서는 캐릭터와 의자에 제어 가능한 회전을 추가하는 방법을 보여줄게요.
+
+
+
어지러워 어지러워!
+
+## 의자 소품 준비하기
+
+이 튜토리얼을 위해 꼭 의자 소품이 필요한 건 아니지만, 의자가 있으면 훨씬 재미있을 거예요. Warudo의 **Discover** 탭을 사용해 Steam Workshop에서 Computer Chair 소품을 다운로드할 수 있어요. (물론, [Mod SDK](../../modding/mod-sdk)를 사용해 직접 의자 소품을 가져올 수도 있어요!)
+
+![](/doc-img/en-blueprint-spinning-16.png)
+
+캐릭터와 의자 소품을 설정해 캐릭터가 의자에 앉도록 해요:
+
+![](/doc-img/en-blueprint-spinning-1.png)
+
+## 캐릭터 회전
+
+카메라 애니메이션 튜토리얼에서 [Animating the Camera](camera) 노드를 기억할 수도 있어요. 하지만 이번에는 캐릭터의 회전만 신경 쓰기 때문에, 대신 **Set Asset Rotation** 노드를 사용할 수 있어요. (참고로, transform은 위치, 회전, 그리고 스케일을 결합한 개념이에요.) 다음 노드가 트리거되면 어떤 일이 벌어질지 추측할 수 있나요?
+
+![](/doc-img/en-blueprint-spinning-2.png)
+
+이 노드는 캐릭터를 (0, 180, 0). 으로 회전시켜요. Y축은 위쪽을 가리키고 있기 때문에, 캐릭터가 머리에서 발까지 막대가 지나가며 180도 회전하는 모습을 상상할 수 있어요. 그 결과, 캐릭터는 이제 의자의 뒤쪽을 향하게 돼요!
+
+:::tip
+그렇다면, 캐릭터를 (180, 0, 0)으로 회전시키면 어떤 일이 일어날까요? 또는 (0, 0, 180)은 어떨까요? 직접 시도해 보세요!
+:::
+
+![](/doc-img/en-blueprint-spinning-3.png)
+
+이제 우리의 다음 단계가 무엇인지 직관적으로 알 수 있을 거예요: 이 노드의 **Rotation** 데이터 인풋에 무언가를 연결해야 해요. 이 '무언가'는 시간이 지남에 따라 변하는 값이어야 해요, 그래야 캐릭터가 점진적으로 회전할 수 있거든요. 변수를 사용할 수도 있겠지만, 더 쉬운 방법이 있어요.
+
+잠시 생각해 봅시다. 캐릭터가 처음에 회전 값 (0, 0, 0)을 가지고 있다고 가정해요. 우리는 그 캐릭터를 (0, 180, 0)으로 회전시키고 싶어요. 이걸 어떻게 할 수 있을까요? 직접 (0, 180, 0)으로 회전시킬 수도 있지만, 그런 갑작스러운 회전 변화는 별로 흥미롭지 않아요. 그럼 중간 단계를 하나 더 추가해 볼까요? 처음엔 (0, 90, 0)으로 회전시키고, 조금 후에 (0, 180, 0)으로 회전시키는 거예요. 아니면 더 많은 단계를 추가할 수도 있겠죠: (0, 30, 0), (0, 60, 0), (0, 90, 0), (0, 120, 0), (0, 150, 0), (0, 180, 0). 그보다 더 많은 단계로 나누면 어떨까요? (0, 10, 0), (0, 20, 0), (0, 30, 0), ... , (0, 180, 0). 중요한 건, 충분한 단계가 있다면 캐릭터가 부드럽게 회전하는 것처럼 보일 거예요!
+
+이제 이 시퀀스 (0, 10, 0), (0, 20, 0), (0, 30, 0), ... , (0, 180, 0)을 보면, Y축 값이 매 단계마다 10씩 증가하는 것을 알 수 있어요. 다시 말해, 매 단계마다 현재 회전 값을 알면, 다음 회전 값을 쉽게 계산할 수 있어요: Y축 값에 10을 더하면 돼요!
+
+아래 blueprint가 바로 이 작업을 수행해요:
+
+![](/doc-img/en-blueprint-spinning-5.png)
+
+이를 풀어보자면, 우리는 고정된 회전값 대신에 변화하는 회전값을 사용하고자 하는 거예요. 이 변화하는 회전값은 캐릭터의 현재 회전값을 기반으로 하고 있어요. 현재 회전값 (X, Y, Z)을 세 숫자 X, Y, Z로 분해하고 (**Decompose Vector3** 노드), Y에 10을 더한 다음 (**Float Addition** 노드), X와 Z는 그대로 두고 Y만 변경하여 새로운 회전값을 만들어요 (**Vector3** 노드); 마지막으로 **Set Asset Rotation** 노드를 통해 캐릭터의 회전값을 새로 설정하는 거죠.
+
+이제 Set Asset Rotation 노드의 Enter 플로우 인풋을 클릭해 보면, 캐릭터가 한 번 클릭할 때마다 10도씩 회전하는 것을 볼 수 있을 거예요!
+
+캐릭터가 자동으로 회전하도록 하려면, 매 프레임마다 트리거되는 **On Update** 노드를 사용하면 돼요:
+
+![](/doc-img/en-blueprint-spinning-6.png)
+
+:::tip
+캐릭터가 더 빠르게 회전하게 하려면 무엇을 변경해야 할까요? 더 느리게 하려면 어떻게 해야 할까요? 단축키로 회전 속도를 제어할 방법을 생각해 볼 수 있나요? (힌트: [variable](squashing#variables)를 사용해 회전 속도를 저장할 수 있어요.)
+:::
+
+하지만 이건 캐릭터만 회전시킬 뿐이에요. 그래서 의자도 함께 회전시켜야 해요:
+
+![](/doc-img/en-blueprint-spinning-7.png)
+
+이제 캐릭터와 의자가 팽이처럼 회전할 거예요!
+
+![](/doc-img/en-blueprint-spinning-8.png)
+
+:::info
+선택한 의자 소품은 기본적으로 캐릭터와 동일한 방향을 가지고 있을 거예요. 그렇지 않다면, 의자를 올바른 방향으로 회전시키기 위한 추가 노드가 필요할 수 있어요.
+:::
+
+## 게이트
+
+이제 캐릭터가 최대 속도로 회전하고 있어요. 그런데 이 회전을 어떻게 멈출 수 있을까요? 물론 On Update 노드를 끊으면 멈출 수 있죠:
+
+![](/doc-img/en-blueprint-spinning-9.png)
+
+하지만 이 방법은 불편하죠, blueprint를 비활성화하는 방법도 있지만, 좀 더 좋은 방법을 찾아봐요. On Update 노드에서 나오는 플로우를 멈출 방법을 찾아보도록 해요.
+
+당신이 성에 살고 있는데 성 안으로 들어오는 사람들의 흐름을 멈추고 싶다고 상상해보세요. 어떻게 하겠어요? 문을 달면 되겠죠!
+
+이와 같은 방식으로, **Gate** 노드를 사용해서 On Update 노드에서 나오는 플로우를 멈출 수 있어요.
+
+![](/doc-img/en-blueprint-spinning-10.png)
+
+Gate 노드는 이름 그대로 동작해요. Enter 플로우 인풋을 통해 들어오는 플로우는 게이트가 열려 있을 때만 Exit 플로우 아웃풋으로 나가요. 기본적으로 게이트는 열려 있지만(**Opened**가 Yes로 설정됨), **Close** 플로우 인풋을 클릭하면 게이트가 닫히고 (**Opened**가 No로 설정됨), 플로우가 나가는 걸 막아요.
+
+![](/doc-img/en-blueprint-spinning-11.png)
+
+즉, 우리는 단축키를 설정해서 이 게이트를 토글할 수 있어요! 아래 blueprint를 사용하면 R 키를 눌러 게이트를 열거나 닫을 수 있고, 캐릭터의 회전을 시작하거나 멈출 수 있어요:
+
+![](/doc-img/en-blueprint-spinning-12.png)
+
+## 회전 초기화
+
+현재 설정에는 문제가 하나 있어요. R 키를 눌러 회전을 멈추면 의자와 캐릭터가 회전 중이어도 바로 멈추게 되죠. 우리는 회전이 끝나고 자연스럽게 원래 위치로 돌아가길 원할 거예요.
+
+이 문제를 해결하기 위해 아래 노드를 추가 해보도록 해요:
+
+![](/doc-img/en-blueprint-spinning-13.png)
+
+이 노드들이 하는 일은 간단해요. T 키를 누르면 캐릭터와 의자가 원래 회전 상태로 부드럽게 돌아가요. 여기서 **Transition Time**을 0.5초로 설정하고, **Transition Easing**을 OutBack으로 설정했어요. 이렇게 하면 회전이 끝날 때 살짝 튀는 듯한 "탄성" 효과가 생기죠.
+
+그런데 잠깐, 왜 (0, 0, 0) 대신 (0, 360, 0)으로 회전하냐고요? 그 이유는 Set Asset Rotation 노드가 항상 목표 회전 값까지 가장 짧은 거리로 회전하기 때문이에요. 예를 들어, 현재 시계 방향으로 90도 회전한 상태에서 0도로 돌아가려면, 반시계 방향으로 90도 돌리거나 시계 방향으로 270도 돌릴 수 있어요. Set Asset Rotation 노드는 가장 짧은 거리인 반시계 방향 90도를 선택하겠죠. 하지만 실제로 의자에 앉아 회전할 때는 가장 짧은 거리가 아니라, 계속 시계 방향으로 돌아서 원래 위치로 돌아가고 싶을 거예요.
+
+그래서 우리는 Set Asset Rotation 노드를 "속여서" 목표 회전을 (0, 360, 0)으로 설정해요. Get Asset Rotation 노드가 반환하는 값은 항상 360도 이하이기 때문에, Set Asset Rotation 노드는 시계 방향으로 계속 돌아가 360도에 도달하게 되는 거죠.
+
+## 시퀀스
+
+거의 완벽한 설정이에요, 하지만 아주 작은 문제가 하나 있어요. 현재 회전을 멈추려면 R을 누르고, T를 눌러야 해요. 그런데 한 번만 R을 눌러서 회전을 멈출 수 있으면 좋겠죠?
+
+처음 보기엔 간단해 보이는 작업이에요. 우리는 Flip Flop 노드를 잘 알고 있죠. 이 노드는 두 가지 플로우 아웃풋을 번갈아가며 실행해요. 그래서 Flip Flop 노드를 사용해서 "게이트를 열고 회전을 시작"과 "게이트를 닫고 회전을 멈추지만, 원래 회전 상태로 돌아가기"를 번갈아 실행하면 될 것 같아요. 첫 번째는 간단해요: Flip Flop → A를 Gate → Open에 연결하면 돼요.
+
+그런데 두 번째는 어떻게 해야 할까요? 만약 Flip Flop → B를 Gate → Close에 연결한다고 해도, Set Asset Rotation 노드를 사용해 원래 회전 상태로 돌아가는 작업은 어떻게 트리거할까요? 플로우 아웃풋은 하나의 플로우 인풋에만 연결할 수 있기 때문에 Flip Flop → B를 Gate → Close와 Set Asset Rotation → Enter에 동시에 연결할 수는 없어요.
+
+![](/doc-img/en-blueprint-spinning-14.png)
+
+이때 필요한 것이 바로 **Sequence** 노드예요. Sequence node노드는 가변적인 수의 플로우 아웃풋을 가질 수 있고, 트리거될 때 각 플로우 아웃풋을 순서대로 실행해요. 시퀀스 노드를 사용하면, 먼저 게이트를 닫고, 그 다음에 원래 회전 상태로 돌아가게 만들 수 있어요:
+
+![](/doc-img/en-blueprint-spinning-15.png)
+
+이제 R만 누르면 회전을 시작하거나, 자연스럽게 회전을 멈출 수 있어요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/squashing.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/squashing.md
new file mode 100644
index 0000000..2d7cfe7
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/squashing.md
@@ -0,0 +1,138 @@
+---
+sidebar_position: 90
+---
+
+# 소품으로 머리 찌그러뜨리기
+
+우리는 이미 [뼈대 스케일](balloon)을 가지고 놀고 있으니, 이번 튜토리얼에서는 한 걸음 더 나아가서 캐릭터의 머리를 납작하게 찌그러뜨려 볼게요!
+
+
+
납작해졌다고 할 수 있겠네요.
+
+## 첫 번째 시도
+
+우리가 익숙한 것부터 시작해 봐요: On Keystroke Pressed 노드와 Throw Prop At Character 노드를 사용하는 거예요. (물론, On Keystroke Pressed 대신 원하는 이벤트 노드를 사용할 수 있지만, 간단하게 하기 위해 이걸로 유지할게요.) 여기서 **Throw Prop At Character → From**을 **Above Character Head**로 설정하고, 거리는 5로 설정했어요. 이렇게 하면 소품이 캐릭터의 머리 위에서 떨어지게 돼요.
+
+![](/doc-img/en-blueprint-squashing-1.png)
+
+지난 튜토리얼에서 했던 것처럼 **Set Character Bone Scale** 노드를 사용해서 소품이 캐릭터와 충돌할 때 머리 뼈대를 스케일할 수 있어요, 이렇게요:
+
+![](/doc-img/en-blueprint-squashing-2.png)
+
+이제 C를 누르면 소품이 캐릭터 머리 위에서 떨어지고, 충돌 시 캐릭터의 머리가 납작하게 찌그러져요.
+
+또한, 머리 뼈대를 원래 크기로 되돌리기 위한 **Set Character Bone Scale** 노드를 따로 추가할 수도 있어요:
+
+![](/doc-img/en-blueprint-squashing-8.png)
+
+언제든지 Enter 플로우 인풋을 클릭하면 머리 뼈대 스케일을 리셋할 수 있어요! (혹은 원한다면 단축키를 설정할 수도 있어요.) 이 방법은 아래 섹션에서 머리 스케일을 리셋하고 싶을 때 매우 유용할 거예요.
+
+## Multi Gate
+
+이제 캐릭터의 머리를 납작하게 만들고 다시 원래대로 돌리는 방법을 알았어요. 문제는 머리가 점점 더 납작해지도록 진행이 필요하다는 거예요. C를 누를 때마다 머리가 더 납작해지도록 만들고 싶다면 어떻게 해야 할까요?
+
+**Multi Gate** 노드가 도움이 돼요! 지금까지 본 노드들과 달리, Multi Gate 노드는 여러 개의 플로우 아웃풋을 가질 수 있어 원하는 만큼 아웃풋을 추가할 수 있죠. 아래처럼 **Exit Count**를 4로 설정하면 Exit 1부터 Exit 4까지 4개의 플로우 아웃풋이 생겨요:
+
+![](/doc-img/en-blueprint-squashing-3.png)
+
+Multi Gate가 트리거될 때마다 **다음 플로우 아웃풋**이 트리거돼요. 예를 들어, 첫 번째로 트리거되면 Exit 1이 실행되고, 두 번째로 트리거되면 Exit 2가 실행되며, 이런 식으로 진행됩니다. 다섯 번째로 트리거되면 Exit 5가 없으므로 다시 Exit 1이 실행되고, 이 패턴이 반복돼요.
+
+바로 이게 우리가 필요한 기능이에요! Multi Gate가 첫 번째로 트리거되면 머리 뼈대 스케일을 1로 리셋하고, 두 번째로 트리거되면 조금 눌러 찌그러뜨리고, 세 번째로는 더 찌그러뜨리고, 이런 식으로 진행할 수 있어요. 이를 blueprint로 표현하면 아래와 같아요:
+
+![](/doc-img/en-blueprint-squashing-4.png)
+
+처음에는 기본 스케일 (1, 1, 1)에서 시작하고, 조금 찌그러뜨리면 (1.33, 0.83, 1.33), 그다음 (1.66, 0.67, 1.66), 마지막으로 (2, 0.5, 2)로 찌그러뜨려요. X축과 Z축을 늘리고 Y축을 줄여서 머리가 납작해지는 거죠. 물론 X와 Z를 1로 유지하고 Y축만 줄일 수도 있지만, 이 방식이 머리를 납작하게 만드는 데 더 자연스러워 보여요.
+
+이제 C를 여러 번 눌러보면 머리가 점점 더 납작해지면서 4번마다 리셋되는 걸 볼 수 있을 거예요!
+
+## variable(변수) {#variables}
+
+오늘은 여기서 마무리해도 되지만, 우리는 항상 더 흥미로운 것을 원하잖아요. 만약 머리를 10번, 아니면 100번 찌그러뜨리고 싶다면 어떻게 해야 할까요? 100개의 플로우 아웃풋과 100개의 Set Character Bone Scale 노드를 만드는 건 좋은 방법이 아니에요. 더 나은 방법이 있을 거예요.
+
+이때 **variable**(변수)가 도움이 돼요.variable은 노드들이 변경하거나 읽을 수 있는 데이터예요. 위의 blueprint에서 4개의 Set Character Bone Scale 노드를 만들었지만, 각 노드의 설정은 **Scale** 옵션만 다를 뿐 본질적으로 같아요. 그래서 많은 노드를 만들 필요 없이, 하나의 노드를 만들고 그 노드가 변경되는 값(즉, variable)을 Scale로 사용하게 할 수 있어요!
+
+우선 variable을 만들어 봅시다. 노드 편집기의 **Properties** 탭으로 가서 **+** 버튼을 눌러 variable을 생성해요. variable 이름을 **BumpedTimes**로 하고, 타입은 **Integer**로 설정해요.(integer은 자연수를 멋지게 표현한 것 뿐이에요. 머리를 1.5번이나 2.3번 찌그러뜨릴수는 없으니까요):
+
+![](/doc-img/en-blueprint-squashing-5.png)
+
+Throw Prop At Character 노드 바로 뒤에 **Integer Variable Add** 노드를 사용해 소품이 캐릭터와 충돌할 때마다 variable을 1씩 증가시켜요:
+
+![](/doc-img/en-blueprint-squashing-6.png)
+
+Warudo로 돌아가서 C를 몇 번 눌러보면, 소품이 캐릭터와 충돌할 때마다 variable이 1씩 증가하는 것을 확인할 수 있어요:
+
+![](/doc-img/en-blueprint-squashing-7.png)
+
+이 variable을 읽기 위해 **Get Integer Variable** 노드를 사용할 수 있어요. Properties 탭에서 **BumpedTimes** variable을 0으로 초기화하고, Set Character Bone Scale, Get Integer Variable, 그리고 Scale Vector3 노드를 아래와 같이 추가해 봅시다. **Scale Vector3 → Scale**은 (1, 1, 1)로 설정되어 있어요:
+
+![](/doc-img/en-blueprint-squashing-9.png)
+
+:::tip
+다음 내용을 읽기 전에, 이 blueprint가 어떤 역할을 할지 예상이 되나요?
+:::
+
+이제 C를 누르면, 소품이 캐릭터와 충돌할 때마다 머리 뼈대가 점점 커질 거예요! 이건 논리적으로 맞아요. 왜냐하면, 소품이 충돌할 때마다 BumpedTimes를 1씩 증가시키고, 머리 뼈대 스케일을 (1, 1, 1)에 BumpedTimes를 곱한 값으로 설정하고 있기 때문이에요. 첫 번째 충돌 시 BumpedTimes는 1이고, 머리 뼈대 스케일은 (1, 1, 1) × 1 = (1, 1, 1)가 돼요. 두 번째 충돌 시 BumpedTimes는 2이고, 머리 뼈대 스케일은 (1, 1, 1) × 2 = (2, 2, 2)가 돼요. 이런 식으로 계속 커지는 거죠.
+
+하지만 우리는 머리를 풍선처럼 키우는 게 아니라 찌그러뜨리고 싶어요! 아래의 blueprint를 다시 만들어 보세요.
+
+![](/doc-img/en-blueprint-squashing-10.png)
+
+Properties 탭에서 BumpedTimes variable을 0으로 리셋하고, C를 몇 번 눌러보세요. 머리 뼈대가 점점 더 납작하게 찌그러지는 걸 볼 수 있을 거예요! 그런데 이게 어떻게 작동할까요?
+
+개인적으로 blueprint를 읽을 때 사용하는 트릭 중 하나는 데이터를 오른쪽에서 왼쪽으로 따라가는 거예요. 이 경우, 최종 스케일 값을 Set Character Bone Scale 노드로 출력하는 **Vector3** literal literal 노드부터 시작해요. 여기서 X와 Z 값은 위쪽 **Float Addition** 노드에서 제공되고, Y 값은 아래쪽 Float Addition 노드에서 제공돼요. 위쪽 경로인 **Get Integer Variable → Float Multiplication → Float Addition** 은 본질적으로 (BumpedTimes × 0.1 + 1)을 계산하고, 아래쪽 경로는 (BumpedTimes × -0.1 + 1)을 계산해요.
+
+:::tip
+우리가 Float Multiplication/Addition 노드를 사용하는 이유는 최종 결과가 소수점 값(float)이기 때문이에요. 정수 값(integer)이 아니죠.
+:::
+
+이제 BumpedTimes의 다른 값에 대한 최종 스케일을 수동으로 계산해 볼게요:
+
+| BumpedTimes | X and Z | Y | Final Scale |
+| ----------- | ------- | - | ----------- |
+| 1 | 1 × 0.1 + 1 = 1.1 | 1 × -0.1 + 1 = 0.9 | (1.1, 0.9, 1.1) |
+| 2 | 2 × 0.1 + 1 = 1.2 | 2 × -0.1 + 1 = 0.8 | (1.2, 0.8, 1.2) |
+| 3 | 3 × 0.1 + 1 = 1.3 | 3 × -0.1 + 1 = 0.7 | (1.3, 0.7, 1.3) |
+| 4 | 4 × 0.1 + 1 = 1.4 | 4 × -0.1 + 1 = 0.6 | (1.4, 0.6, 1.4) |
+
+이 패턴이 보이시죠? X와 Z 값은 매번 0.1씩 증가하고, Y 값은 매번 0.1씩 감소해요. 바로 이게 우리가 원하는 거예요: 소품이 충돌할 때마다 머리 뼈대가 점점 더 납작하게 찌그러지게 되는 거죠!
+
+## Variable 리셋
+
+하지만 C를 몇 번 더 누르면, 결국 머리 뼈대가 반대 방향으로 커지는 것을 볼 수 있을 거예요, 이런 식으로요:
+
+![](/doc-img/en-blueprint-squashing-11.png)
+
+왜 그럴까요? 위에서 계산을 계속해 보면:
+
+| BumpedTimes | X and Z | Y | Final Scale |
+| ----------- | ------- | - | ----------- |
+| 8 | 8 × 0.1 + 1 = 1.8 | 8 × -0.1 + 1 = 0.2 | (1.8, 0.2, 1.8) |
+| 9 | 9 × 0.1 + 1 = 1.9 | 9 × -0.1 + 1 = 0.1 | (1.9, 0.1, 1.9) |
+| 10 | 10 × 0.1 + 1 = 2 | 10 × -0.1 + 1 = 0 | (2, 0, 2) |
+| 11 | 11 × 0.1 + 1 = 2.1 | 11 × -0.1 + 1 = -0.1 | (2.1, -0.1, 2.1) |
+| 12 | 12 × 0.1 + 1 = 2.2 | 12 × -0.1 + 1 = -0.2 | (2.2, -0.2, 2.2) |
+
+Y 값이 음수가 되고 있어요, 즉 머리 뼈대가 아래쪽으로 자라기 시작하는 거예요! 우리가 원하는 결과는 아니죠. BumpedTimes가 10이 되면 머리 뼈대 스케일을 (1, 1, 1)로 리셋해야 해요. 10이 가장 평평해질 수 있는 값이고, 그 이상 가면 머리가 아래로 자라게 됩니다. 다행히도 우리는 이미 이것을 해결할 방법을 알고 있어요: [댄스](dance#if-branch)튜토리얼에서 배운 If Branch 노드를 사용하면 돼요!
+
+![](/doc-img/en-blueprint-squashing-12.png)
+
+새로 추가된 노드는 매우 간단해요. BumpedTimes가 10 이상인지 확인하고, 그렇다면 이를 0으로 리셋하며, 동시에 머리 뼈대 스케일도 다시 1로 리셋하는 거예요.
+
+:::tip
+여기서는 **Integer Greater Than Or Equal** 노드를 사용하지만, **Integer Equal** 노드를 사용해 BumpedTimes가 10과 같은지 확인할 수도 있어요. 차이점은 전자는 BumpedTimes가 10, 11, 12일 때 If True 플로우 아웃풋을 트리거하지만, 후자는 BumpedTimes가 정확히 10일 때만 트리거해요. 일반적으로 전자가 더 유연하기 때문에 많이 사용돼요. 예를 들어, BumpedTimes가 실수로 11이 된다면, 후자를 사용했을 때는 절대로 다시 0으로 리셋할 수 없겠죠.
+:::
+
+최종 blueprint는 이렇게 생겼어요 (꽤 크죠!):
+
+![](/doc-img/en-blueprint-squashing-13.png)
+
+축하해요! 이제 blueprint의 variable 개념을 익혔어요. 이 튜토리얼은 이전 것들보다 조금 더 도전적이었는데, 여기까지 온 당신, 정말 대단해요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/tail.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/tail.md
new file mode 100644
index 0000000..66ce554
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/tutorials/tail.md
@@ -0,0 +1,93 @@
+---
+sidebar_position: 140
+---
+
+# 꼬리 잡기
+
+캐릭터에 꼬리가 있나요? 그렇다면 이번 튜토리얼이 딱 맞을 거예요! 이번 튜토리얼에서는 단축키를 누르면 캐릭터가 꼬리를 잡도록 설정할 거예요.
+
+
+
꼬리를 잡아보자!
+
+:::tip
+꼬리가 없는 캐릭터라 해도 괜찮아요! 이 튜토리얼을 읽고 **Set Asset Property**와 **Invoke Asset Trigger** 노드를 사용하는 방법을 배울 수 있어요.
+:::
+
+## 꼬리 IK
+
+blueprint를 작업하기 전에 먼저 씬을 설정할게요! 먼저 [앵커](../../assets/anchor) 에셋을 생성하고 캐릭터의 오른손에 부착하세요:
+
+![](/doc-img/en-blueprint-tail-2.png)
+
+이 설정은 나중에 꼬리를 캐릭터의 오른손으로 향하게 할 때 유용해요.
+
+다음으로 [꼬리](../../assets/tail) 에셋을 만들어 캐릭터에 [설정](../../assets/tail#setup)하세요. **Inverse Kinematics** 섹션에서 **Enabled**을 Yes로 설정하고, **IK Target**을 방금 만든 앵커로 설정하세요:
+
+![](/doc-img/en-blueprint-tail-3.png)
+
+:::info
+이미 꼬리를 만들었다면 이번 튜토리얼에서는 흔들기와 추가 셰이핑을 비활성화해야 해요.
+:::
+
+이 설정을 통해 꼬리가 역운동학(IK)을 사용하여 앵커를 향하게 돼요. 다시 말해, 꼬리는 이런식으로 항상 캐릭터의 오른손을 향하게 될 거예요:
+
+![](/doc-img/en-blueprint-tail-1.png)
+
+## 에셋 속성 설정
+
+현재는 꼬리를 잡는 것과 잡지 않는 것을 토글하려면 **Tail → Inverse Kinematics → Enabled** 옵션을 수동으로 켜고 꺼야 해요.
+
+![](/doc-img/en-blueprint-tail-4.png)
+
+이걸 blueprint에서 할 수 있는 방법이 있을까요? 아쉽게도 "Toggle Tail IK"라는 노드는 없어요. 하지만 다행히도 강력하고 유연한 **Set Asset Property** 노드를 사용하면 원하는 작업을 할 수 있어요!
+
+이 노드는 이름 그대로 에셋의 속성(옵션)을 설정해줘요. Tail 에셋을 **Asset** 드롭다운에서 선택한 후, **Data Path** 드롭다운에서 Inverse Kinematics → Enabled 속성을 선택하면 돼요:
+
+![](/doc-img/en-blueprint-tail-9.png)
+
+그다음 **Target Value**를 지정하는 대신 **Toggle**을 Yes로 설정하면 이 노드는 매번 트리거될 때마다 속성을 Yes와 No 사이에서 토글할 거예요:
+
+![](/doc-img/en-blueprint-tail-5.png)
+
+이제 Ctrl+Shift+X를 누르면 꼬리 IK가 켜졌다 꺼졌다 할 거예요! 간단하죠?
+
+꼬리 IK를 켜고 끄는 건 좋지만, IK가 즉시 켜지거나 꺼지는 것을 눈치챘을 거예요. 꼬리가 손에 바로 닿기보다는 서서히 닿도록, IK를 점진적으로 켜고 끄면 더 자연스러울 거예요.
+
+이를 위해 **Tail → Inverse Kinematics → Enabled**을 Yes로 설정하고, **Tail → Inverse Kinematics → Weight** 옵션을 조절하면 돼요:
+
+![](/doc-img/en-blueprint-tail-6.png)
+
+만약 꼬리의 가중치를 0에서 1로 끌어올리면 꼬리가 점차 손으로 향하는 것을 볼 수 있어요. 그럼 이걸 blueprint에서 어떻게 할 수 있을까요? 또다시 **Set Asset Property** 노드를 사용하면 돼요! 이번에는 select the Tail → Inverse Kinematics → Weight property 속성을 선택해요:
+
+![](/doc-img/en-blueprint-tail-8.png)
+
+그다음 두 노드에서 각각 **Target Value**를 1과 0으로 설정해요. Flip Flop 노드가 매번 트리거될 때마다 두 Exit 플로우 아웃풋을 번갈아 가며 실행하니까, Ctrl+Shift+X를 누르면 꼬리 IK가 점차 켜지거나 꺼지게 돼요.
+
+![](/doc-img/en-blueprint-tail-7.png)
+
+:::tip
+보시다시피 Set Asset Property 노드는 에셋 탭에서 보이는 거의 모든 것을 설정할 수 있어요! 때로는 전용 노드를 대체하기도 해요. 예를 들어 Set Asset Property 노드는 소품의 **Enabled** 옵션도 설정할 수 있어서 더 이상 **Toggle Asset Enabled** 노드를 쓸 필요가 없을지도 몰라요.
+
+하지만 Set Asset Property는 보다 일반적인 노드라서 Toggle Asset Enabled 같은 전용 노드보다 성능이 떨어져요. 특히 Set Asset Property 노드를 너무 자주 트리거하지 않도록 주의해야 해요(**On Update** 노드 뒤에 배치하는 경우). 그렇지 않으면 성능 문제가 발생할 수 있어요. 기본 원칙은 원하는 작업에 전용 노드가 있다면 Set Asset Property 대신 그 전용 노드를 사용하는 게 좋아요.
+:::
+
+## 에셋 트리거 호출
+
+이제 슬라이더 조정과 스위치 토글을 자동화하는 방법은 알게 되었어요. 그렇다면 버튼 클릭도 자동화할 수 있을까요? 예를 들어, **Tail → Reset Tail** 버튼을 단축키로 자동으로 "클릭"할 수 있는 방법이 있을까요?
+
+![](/doc-img/en-blueprint-tail-11.png)
+
+물론 있어요! **Invoke Asset Trigger** 노드는 바로 이 작업을 해줘요. Set Asset Property 노드와 비슷하지만, 속성을 설정하는 대신 에셋의 트리거(버튼)를 호출해요. **Trigger Path** 드롭다운에서 Tail → Reset Tail 트리거를 선택하면 돼요:
+
+![](/doc-img/en-blueprint-tail-10.png)
+
+Warudo에는 꽤 많은 버튼이 있으니, 이 노드는 여러 상황에서 유용할 거예요! 예를 들어, iFacialMocap이나 MediaPipe를 얼굴 추적에 사용하는 경우, 저는 **iFacialMocap Receiver / MediaPipe Tracker → Calibrate**에 단축키를 할당해서 얼굴 추적을 단축키로 바로 교정할 수 있게 설정하는 걸 추천해요.
+
+
From a99c15ad3918ff33113191912df195a70ade579f Mon Sep 17 00:00:00 2001
From: Willycho <47117325+Willycho@users.noreply.github.com>
Date: Tue, 24 Sep 2024 19:41:40 +0900
Subject: [PATCH 2/2] Translation
Finally got everything done! Please update quickly for review. Thanks!
---
.../blueprints/nodes/math-expression.md | 115 +++++
.../modding/character-animation-mod.md | 42 ++
.../current/modding/character-mod.md | 94 ++++
.../modding/creating-your-first-mod.md | 94 ++++
.../current/modding/environment-mod.md | 97 ++++
.../current/modding/mod-sdk.md | 71 +++
.../current/modding/particle-mod.md | 32 ++
.../current/modding/prop-mod.md | 40 ++
.../current/modding/sdk-installation.md | 203 ++++++++
.../current/modding/sharing.md | 32 ++
.../current/scripting/api/assets.md | 174 +++++++
.../current/scripting/api/entities.md | 67 +++
.../current/scripting/api/events.md | 62 +++
.../scripting/api/generating-blueprints.md | 70 +++
.../current/scripting/api/io.md | 63 +++
.../current/scripting/api/localization.md | 79 ++++
.../current/scripting/api/mixins.md | 110 +++++
.../current/scripting/api/nodes.md | 170 +++++++
.../current/scripting/api/overview.md | 29 ++
.../current/scripting/api/plugins.md | 158 +++++++
.../scripting/api/ports-and-triggers.md | 444 ++++++++++++++++++
.../api/resource-providers-and-resolvers.md | 185 ++++++++
.../current/scripting/api/scene.md | 45 ++
.../current/scripting/api/service.md | 37 ++
.../current/scripting/api/structured-data.md | 317 +++++++++++++
.../current/scripting/api/watchers.md | 107 +++++
.../creating-your-first-plugin-mod.md | 197 ++++++++
.../scripting/creating-your-first-script.md | 164 +++++++
.../current/scripting/debugging.md | 36 ++
.../current/scripting/overview.md | 69 +++
.../current/scripting/playground.md | 46 ++
.../current/scripting/plugin-mod.md | 48 ++
32 files changed, 3497 insertions(+)
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/blueprints/nodes/math-expression.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/character-animation-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/character-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/creating-your-first-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/environment-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/mod-sdk.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/particle-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/prop-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/sdk-installation.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/modding/sharing.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/assets.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/entities.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/events.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/generating-blueprints.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/io.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/localization.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/mixins.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/nodes.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/overview.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/plugins.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/ports-and-triggers.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/resource-providers-and-resolvers.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/scene.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/service.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/structured-data.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/watchers.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-plugin-mod.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-script.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/debugging.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/overview.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/playground.md
create mode 100644 i18n/ko/docusaurus-plugin-content-docs/current/scripting/plugin-mod.md
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/nodes/math-expression.md b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/nodes/math-expression.md
new file mode 100644
index 0000000..83537b3
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/blueprints/nodes/math-expression.md
@@ -0,0 +1,115 @@
+---
+sidebar_position: 100
+---
+
+# 수학 표현식
+
+## 지원되는 함수
+
+```
+deg => x * Mathf.Rad2Deg
+rad => x * Mathf.Deg2Rad
+sin => Mathf.Sin
+cos => Mathf.Cos
+tan => Mathf.Tan
+asin => Mathf.Asin
+acos => Mathf.Acos
+atan => Mathf.Atan
+atan2 => Mathf.Atan2
+noise => Mathf.PerlinNoise
+sqrt => Mathf.Sqrt
+abs => Mathf.Abs(float)
+min => Mathf.Min(float, float)
+max => Mathf.Max(float, float)
+pow => Mathf.Pow(float, float)
+exp => Mathf.Exp
+log => Mathf.Log(float, float)
+ln => Mathf.Log(float)
+log10 => Mathf.Log10
+ceil => Mathf.Ceil
+floor => Mathf.Floor
+round => Mathf.Round
+sign => Mathf.Sign
+clamp => Mathf.Clamp(float, float, float)
+clamp01 => Mathf.Clamp01
+lerp => Mathf.Lerp(float, float, float)
+ulerp => Mathf.LerpUnclamped(float, float, float)
+alerp => Mathf.LerpAngle(float, float, float)
+smoothstep => Mathf.SmoothStep(float, float, float)
+repeat => Mathf.Repeat(float, float)
+pingpong => Mathf.PingPong(float, float)
+ilerp => Mathf.InverseLerp(float, float, float)
+rand => Random.Range(float, float)
+randint => Random.Range(int, int)
+```
+
+## 세부 테이블
+
+### 단항 함수
+
+| 함수 | 유니티 코드 | 설명 |
+|:---------- |:---------------- |:-------------------------------------------------------- |
+| `sqrt(x)` | `Mathf.Sqrt(x)` | x의 제곱근을 계산해 줍니다. |
+| `abs(x)` | `Mathf.Abs(x)` | x의 절대값을 계산해 줍니다. |
+| `ceil(x)` | `Mathf.Ceil(x)` | x보다 크거나 같은 가장 가까운 정수를 찾아 줍니다. |
+| `floor(x)` | `Mathf.Floor(x)` | x보다 작거나 같은 가장 가까운 정수를 찾아 줍니다. |
+| `round(x)` | `Mathf.Round(x)` | x를 가장 가까운 정수로 반올림해 줍니다. |
+| `sign(x)` | `Mathf.Sign(x)` | x가 양수면 1, 음수면 -1을 알려 줍니다. |
+
+### 삼각 함수
+
+| 함수 | 유니티 코드 | 설명 |
+|:------------- |:------------------- |:----------------------------------------------------------- |
+| `deg(x)` | `x * Mathf.Rad2Deg` | 라디안 단위의 각도를 도(degree) 단위로 바꿔줍니다. |
+| `rad(x)` | `x * Mathf.Deg2Rad` | 도(degree) 단위의 각도를 라디안 단위로 바꿔줍니다. |
+| `sin(x)` | `Mathf.Sin(x)` | 각도 x(라디안 단위)의 사인(sine) 값을 계산해 줍니다. |
+| `cos(x)` | `Mathf.Cos(x)` | 각도 x(라디안 단위)의 코사인(cosine) 값을 계산해 줍니다. |
+| `tan(x)` | `Mathf.Tan(x)` | 각도 x(라디안 단위)의 탄젠트(tangent) 값을 계산해 줍니다. |
+| `asin(x)` | `Mathf.Asin(x)` | x의 역사인(arc sine) 값을 라디안 단위로 계산해 줍니다. |
+| `acos(x)` | `Mathf.Acos(x)` | x의 역코사인(arc cosine) 값을 라디안 단위로 계산해 줍니다. |
+| `atan(x)` | `Mathf.Atan(x)` | x의 역탄젠트(arc tangent) 값을 라디안 단위로 계산해 줍니다. |
+| `atan2(x, y)` | `Mathf.Atan2(x, y)` | v1=(0,1)과 v2=(x,y) 두 벡터 사이의 각도를 라디안 단위로 계산해 줍니다. |
+
+### 지수 및 로그 함수
+
+| 함수 | 유니티 코드 | 설명 |
+|:----------- |:----------------- |:-------------------------------------------- |
+| `pow(x, y)` | `Mathf.Pow(x, y)` | x를 y 제곱한 값을 계산해 줍니다. |
+| `exp(x)` | `Mathf.Exp(x)` | e(자연 상수)를 x 제곱한 값을 계산해 줍니다. |
+| `log(x, y)` | `Mathf.Log(x, y)` | x의 y를 밑(base)으로 하는 로그 값을 계산해 줍니다. |
+| `ln(x)` | `Mathf.Log(x)` | x의 자연 로그(밑이 e인 로그) 값을 계산해 줍니다. |
+| `log10(x)` | `Mathf.Log10(x)` | x의 밑이 10인 로그 값을 계산해 줍니다. |
+
+### 보간 함수
+
+| 함수 | 유니티 코드 | 설명 |
+|:--------------------- |:------------------------------ |:---------------------------------------------------------------------------- |
+| `lerp(x, y, t)` | `Mathf.Lerp(x, y, t)` | t에 따라 x와 y 사이를 선형으로 보간(중간 값을 계산)해 줍니다. |
+| `ulerp(x, y, t)` | `Mathf.LerpUnclamped(x, y, t)` | t에 따라 x와 y 사이를 제한 없이 선형으로 보간해 줍니다. |
+| `alerp(x, y, t)` | `Mathf.LerpAngle(x, y, t)` | 각도 x와 y 사이를 t에 따라 보간해 줍니다. |
+| `smoothstep(x, y, t)` | `Mathf.SmoothStep(x, y, t)` | t에 따라 x와 y 사이를 부드럽게 보간해 줍니다. |
+
+
+### 그 외 함수
+
+| 함수 | 유니티 코드 | 설명 |
+|:--------------------- |:-------------------------------- |:---------------------------------------------------------------------------------------------------- |
+| `min(x, y)` | `Mathf.Min(x, y)` | 두 값 중에서 더 작은 값을 골라 줍니다. |
+| `max(x, y)` | `Mathf.Max(x, y)` | 두 값 중에서 더 큰 값을 골라 줍니다. |
+| `noise(x, y)` | `Mathf.PerlinNoise(x, y)` | 주어진 좌표 x와 y에서 페를린 노이즈라는 값을 만들어 줍니다. |
+| `clamp(x, min, max)` | `Mathf.Clamp(x, min, max)` | x가 min보다 작지 않고 max보다 크지 않게 범위 안에 맞춰 줍니다. |
+| `clamp01(x)` | `Mathf.Clamp01(x)` | x가 0보다 작지 않고 1보다 크지 않게 맞춰 줍니다. |
+| `repeat(t, length)` | `Mathf.Repeat(t, length)` | t가 0에서 length 사이에서 계속 반복되도록 해줍니다. |
+| `pingpong(t, length)` | `Mathf.PingPong(t, length)` | t가 0과 length 사이에서 왔다 갔다 반복되도록 해줍니다. |
+| `ilerp(from, to, t)` | `Mathf.InverseLerp(from, to, t)` | from과 to 사이에서 t가 어느 위치에 있는지 계산해 줍니다. |
+| `rand(min, max)` | `Random.Range(min, max)` | min과 max 사이의 무작위 숫자를 골라 줍니다. (실수) |
+| `randint(min, max)` | `Random.Range(min, max)` | min과 max 사이의 무작위 숫자를 골라 줍니다. (정수, max는 포함하지 않음) |
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/character-animation-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/character-animation-mod.md
new file mode 100644
index 0000000..4118a20
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/character-animation-mod.md
@@ -0,0 +1,42 @@
+---
+sidebar_position: 11
+---
+
+# 캐릭터 애니메이션 모드
+
+Warudo에서 사용하고 싶은 [애니메이션 클립](https://docs.unity3d.com/kr/current/Manual/AnimationClips.html)이 있나요? 그렇다면 캐릭터 애니메이션 모드가 필요해요!
+
+:::caution
+만약 비뼈대 변환이나 재질 속성을 애니메이션화하는 애니메이션이 있다면, 다른 방법으로 캐릭터에 이 애니메이션을 추가하는 것이 좋습니다. [캐릭터 모드](character-mod#animations) 자세한 내용은 캐릭터 모드 섹션을 참조하세요.
+:::
+
+## 설정
+
+### 1단계: 애니메이션 클립 준비
+
+이미 프로젝트에 **애니메이션 클립**(청록색 삼각형 아이콘으로 표시)이 있다면, 잘됐어요! 이 단계를 건너뛰어도 됩니다
+
+애니메이션이 FBX 파일인 경우, 먼저 리그 임포트 설정에서 **Animation Type**을 Humanoid로 설정하세요:
+
+![](/doc-img/en-mod-14.png)
+
+그런 다음, FBX 파일을 확장하고 내부의 **애니메이션 클립**(청록색 삼각형 아이콘으로 표시)을 선택한 후 Ctrl+D를 눌러 FBX 파일 밖에 복사본을 만드세요.
+
+![](/doc-img/en-character-animation-mod-1.webp)
+
+### 2단계: 애니메이션 클립 이름 변경
+
+애니메이션 클립의 이름을 **"Animation"**으로 변경하고 모드 폴더(모든 하위 폴더에 넣을 수 있음)에 배치했는지 확인하세요.
+
+### 2단계: 모드 내보내기
+
+**Warudo → Build Mod**를 선택하고, 생성된 `.warudo` 파일이 `CharacterAnimations` 데이터 폴더에 저장되었는지 확인하세요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/character-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/character-mod.md
new file mode 100644
index 0000000..747a3d3
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/character-mod.md
@@ -0,0 +1,94 @@
+---
+sidebar_position: 10
+---
+
+# 캐릭터 모드
+
+Unity로 가져올 수 있는 캐릭터 모델이라면 Warudo에 임포트할 수 있어요! 여기에는 FBX 모델이나 [VRChat 아바타](https://booth.pm/en/search/avatar?tags%5B%5D=3D+Character)도 포함되죠. VRM 모델을 사용하더라도, 캐릭터 모드를 만들면 커스텀 셰이더를 추가하거나 [Dynamic Bone](https://assetstore.unity.com/packages/tools/animation/dynamic-bone-16743)과 [Magica Cloth](https://assetstore.unity.com/packages/tools/physics/magica-cloth-160144) 같은 더 나은 물리 컴포넌트를 적용하는 등의 이점이 있어요.
+
+## 설정
+
+### 1단계: 모델 준비
+
+먼저 캐릭터 모델을 모딩 프로젝트에 임포트하세요. 모델은 [Animator](https://docs.unity3d.com/ScriptReference/Animator.html) 컴포넌트가 있는 휴머노이드 모델이어야 해요. 만약 모델에 Animator 컴포넌트가 없다면, 모델 임포트 설정에서 **Animation Type**을 Humanoid로 설정한 후 **Apply**를 클릭하세요.
+
+![](/doc-img/en-character-mod-1.webp)
+
+만약 FBX 모델을 사용 중이라면, 모델 임포트 설정에서 **R/W Enabled**가 체크되어 있는지 확인하세요.
+
+![](/doc-img/en-character-mod-2.webp)
+
+### 2단계: 캐릭터 설정
+
+씬에 캐릭터를 배치하고 선택하세요. 메뉴 바에서 **Warudo → Setup Character...** 를 선택하세요. Setup Character 창에서 **Setup selected GameObject as character mod**를 선택하세요:
+
+![](/doc-img/en-character-mod-3.webp)
+
+**Character**라는 이름의 프리팹이 모드 폴더에 생성된 것을 확인할 수 있을 거예요:
+
+![](/doc-img/en-character-mod-4.webp)
+
+:::tip
+캐릭터의 뼈대가 꼬이거나 비틀어진 것을 발견했다면 [뼈대 정상화](#normalize-bones) 섹션을 참고하세요.
+:::
+
+### 3단계: 모드 내보내기
+
+**Warudo → Build Mod**를 선택하고, 생성된 `.warudo` 파일이 `Characters` 데이터 폴더에 들어갔는지 확인하세요.
+
+## 스케일링
+
+Warudo에서는 **Character → Transform → Scale** 속성을 사용해 캐릭터의 크기를 조정할 수 있어요. 하지만 이렇게 하면 래그돌, IK, 또는 몇몇 모션 캡처 시스템 (예: [Leap Motion](../mocap/leap-motion))과의 호환성이 깨질 수 있어요. 캐릭터의 크기를 조정해야 할 경우, Unity나 Warudo에 모델을 임포트하기 전에 3D 모델링 소프트웨어에서 미리 조정하는 것을 권장해요.
+
+## 서드파티 컴포넌트
+
+Warudo에는 캐릭터 모드에서 사용할 수 있는 몇 가지 서드파티 컴포넌트가 포함되어 있어요. 자주 사용하는 컴포넌트는 다음과 같아요:
+
+* [VRM](https://vrm.dev/en/univrm/) VRM Spring Bones, VRM Spring Bone Colliders와 같은 컴포넌트
+* [Animation Rigging](https://docs.unity3d.com/Packages/com.unity.animation.rigging@latest) Rotation Constraint 같은 컴포넌트
+* [Dynamic Bone](https://assetstore.unity.com/packages/tools/animation/dynamic-bone-16743) 1.3.2
+* [VRC PhysBones](https://docs.vrchat.com/docs/physbones) (런타임에 자동으로 Dynamic Bone으로 변환됨)
+* [Magica Cloth](https://assetstore.unity.com/packages/tools/physics/magica-cloth-160144) 1.12.11
+* [Magica Cloth 2](https://assetstore.unity.com/packages/tools/physics/magica-cloth-2-242307) 2.6.0
+
+Dynamic Bone과 Magica Cloth는 Warudo SDK에 포함되어 있지 않으므로 해당 Unity 패키지를 직접 임포트해야 해요.
+
+Warudo는 모드에 C# 스크립트를 포함할 수 있기 때문에, 다른 서드파티 Unity 컴포넌트도 사용할 수 있어요! 하지만 [커스텀 C# 스크립트](mod-sdk#custom-scripts) 섹션에서 설명한 제한 사항을 참고해야 해요.
+
+## 애니메이션
+
+캐릭터에 머리 색깔 변경이나 날개 퍼덕임과 같은 커스텀 애니메이션을 추가하고 싶을 수 있어요. 애니메이션의 성격에 따라 다음 방법 중 하나를 사용할 수 있어요:
+
+* **애니메이션이 인체 뼈대만 제어하는 경우:** [캐릭터 애니메이션 모드](character-animation-mod)를 생성하고 Warudo의 캐릭터 애니메이션 노드나 Character → Overlaying Animations를 사용해 재생하세요.
+* **애니메이션이 재질 속성을 제어하는 경우:** Warudo의 캐릭터 표현 시스템 (Character → Expressions)을 사용하세요.
+* **애니메이션 뼈대가 아닌 변형을 제어하는 경우:** Animator 컴포넌트에 [애니메이터 컨트롤러](https://docs.unity3d.com/kr/current/Manual/class-AnimatorController.html)를 추가하고, [Feline's Animator Parameter Setter Nodes](https://steamcommunity.com/sharedfiles/filedetails/?id=3005732826&searchtext=animator+)를 사용해 애니메이터 컨트롤러에 접근하세요.
+
+:::info
+VRChat 모델을 재사용하는 경우, 모델과 함께 제공되는 Animator Controller를 그대로 사용할 수도 있어요. 하지만 처음 두 가지 방법이 Warudo의 모션 캡처 및 애니메이션 시스템과 더 잘 통합되기 때문에 이 방법들을 권장해요.
+:::
+
+## 뼈대 정상화 {#normalize-bones}
+
+뼈대 정상화는 모델의 뼈대가 T-포즈에서 회전 값이 0이 되도록 보장하는 과정이에요. 모델을 Unity로 가져와서 1. 모델이 T-포즈 상태인지, 2. 모든 뼈대 변환의 회전 값이 (0, 0, 0)인지 확인한 후 Warudo SDK를 사용해 캐릭터를 설정할 수 있어요. **Setup Character** 창에서도 뼈대가 정상화되지 않은 경우 경고가 표시돼요:
+
+![](/doc-img/en-mod-11.png)
+
+캐릭터 설정 후 뼈대가 비틀린다면 모델의 뼈대가 정상화되지 않고 Warudo SDK가 모델을 손상시키지 않고 자동으로 뼈대를 정상화하는데 실패했을 가능성이 높아요. 이런 경우 FBX 모델을 Unity로 임포트 하기 전에 수동으로 뼈대를 정상화해야 해요.
+
+Blender나 Maya 같은 모델링 도구에서 뼈대를 정상화하는 여러 방법이 있지만, 가장 검증된 방법은 FBX 모델에서 [VRM](https://vrm.dev/en/univrm/) 모델을 생성한 후, VRM 모델을 Unity로 가져오는 거예요. VRM 모델은 뼈대가 정상화된 상태임이 보장되기 때문에 Warudo SDK로 캐릭터 설정을 한 후에도 뼈대가 비틀리지 않을 거예요.
+
+Blender 사용자인 경우, [Cats](https://github.com/absolute-quantum/cats-blender-plugin)의 **Set Rest Pose**옵션을 사용해 수동으로 뼈대를 정상화할 수 있어요. FBX를 내보낼 때는 다음 설정을 사용해야 해요:
+
+![](/doc-img/en-mod-15.png)
+
+
+Blender에 내장된 FBX 내보내기 기능은 올바른 뼈대 회전 값을 내보내지 못하는 문제가 알려져 있어요. 만약 이 방법이 잘 작동하지 않는다면 [Better FBX Importer & Exporter](https://blendermarket.com/products/better-fbx-importer--exporter)나 [Cats](https://github.com/absolute-quantum/cats-blender-plugin)와 같은 서드파티 애드온을 사용해 FBX 모델을 내보내는 것을 시도해 볼 수 있어요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/creating-your-first-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/creating-your-first-mod.md
new file mode 100644
index 0000000..fb44e2b
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/creating-your-first-mod.md
@@ -0,0 +1,94 @@
+---
+sidebar_position: 5
+---
+
+# 당신의 첫 모드를 만들어보세요
+
+이제 첫 번째 모드를 만들어봐요! 이번 튜토리얼에서는 간단한 흰색 큐브 [소품 모드](prop-mod)를 만들 거예요.
+
+:::info
+시작하기 전에 [Warudo SDK](mod-sdk.md)가 이미 설정되어 있는지 확인하세요..
+:::
+
+Unity를 사용해 본 경험이 없다면, Unity 에디터에 익숙해지기 위해 초보자 튜토리얼을 몇 개 보는 것을 추천해요. [Unity Learn](https://learn.unity.com/)이 좋은 출발점이에요!
+
+:::tip
+처음 모드를 만들 때는 다소 복잡할 수 있어요. 도움이 필요하면 언제든 [Discord](https://discord.gg/warudo)에서 문의하세요!
+:::
+
+
+## Tutorial
+
+새로운 모드를 만들려면 메뉴 바에서 **Warudo → New Mod**를 선택하세요:
+
+![](/doc-img/en-mod-sdk-3.webp)
+
+**Mod Name**에 "WhiteCube"을 입력하고 **Create Mod!**을 클릭하세요:
+
+![](/doc-img/en-mod-1.png)
+
+Assets 폴더 아래에 모드 폴더가 생성된 것을 확인할 수 있어요:
+
+![](/doc-img/en-mod-2.png)
+
+이 폴더는 **mod folder**라고 불리며, 모드와 동일한 이름을 가집니다..
+:::tip
+**모든 assets(예: 프리팹, 셰이더, 재질)와 스크립트는 반드시 모드 폴더에 있어야 해요.** 만약 모드 폴더 밖에 있으면 모드에 포함되지 않아요.
+:::
+
+이제 씬에 큐브를 만들어 봅시다. 메뉴 바에서 **GameObject → 3D Object → Cube**를 선택하세요:
+
+![](/doc-img/en-mod-3.png)
+
+큐브를 모드로 내보내려면, 먼저 모드 폴더에 프리팹을 만들어야 해요. 씬에서 큐브를 선택한 후 모드 폴더로 드래그하여 프리팹(파란색 큐브 아이콘)을 만드세요:
+
+![](/doc-img/en-mod-4.png)
+
+Warudo는 당신이 어떤 유형의 모드를 만드는지 알아야 해요. 우리는 소품 모드를 만들고 있으니, 프리팹 이름을 Prop으로 변경해야 해요. 프리팹을 마우스 오른쪽으로 클릭하고 **Rename**을 선택하세요:
+
+![](/doc-img/en-mod-5.png)
+
+거의 완료됐어요! 모드를 내보내기 전에, 모드 설정이 올바른지 확인해 봅시다. **Warudo → Mod Settings**을 선택하여 모드 설정 창을 열고, 모드의 이름, 버전, 제작자, 설명을 설정할 수 있어요. 또한 Warudo의 미리보기 갤러리에서 표시될 모드 아이콘도 지정할 수 있습니다.
+
+![](/doc-img/en-mod-6.png)
+
+기본적으로 **Mod Export Directory**는 비어 있어요. 이 경우 모드는 프로젝트의 루트 폴더에 내보내지죠. 모드를 Warudo에서 바로 테스트할 수 있도록 해당 Warudo 데이터 폴더로 경로를 설정해 두는 것이 편리해요!
+
+Warudo에서 **Menu → Open Data Folder**를 선택해 데이터 폴더를 열어요. 그런 다음, 데이터 폴더의 경로를 복사하여 **Mod Export Directory** 필드에 붙여넣습니다. 우리가 소품 모드를 만들고 있으니 경로 끝에 `\Props`를 추가해 주세요. 예를 들어, 데이터 폴더의 경로가 `C:\Program Files (x86)\Steam\steamapps\common\Warudo\Warudo_Data\StreamingAssets`라면, **Mod Export Directory**는 `C:\Program Files (x86)\Steam\steamapps\common\Warudo\Warudo_Data\StreamingAssets\Props`가 되어야 해요.
+
+:::tip
+마찬가지로, 캐릭터 모드를 만들고 있다면 **Mod Export Directory**를 `Characters` 데이터 폴더로 설정해야 합니다.
+:::
+
+마지막으로 **Warudo → Build Mod**를 선택하여 모드를 내보내세요. 모드가 내보내지고 나면 props 데이터 폴더에 `WhiteCube.warudo` 파일이 생긴 것을 확인할 수 있을 거예요. 이제 Warudo에서 소품 에셋을 만들고 **Source** 드롭다운에서 "WhiteCube"를 선택해 보세요. 씬에 흰색 큐브가 나타난다면 축하해요! 첫 번째 모드를 성공적으로 만들었어요!
+
+![](/doc-img/en-mod-7.png)
+
+다른 모드를 만드는 과정도 비슷해요: **Warudo → New Mod**로 새 모드를 만들고, 에셋과 스크립트를 모드 폴더에 넣은 후 **Warudo → Build Mod**를 통해 모드를 내보내세요. 다양한 모드 종류에 대한 더 자세한 정보는 사이드바의 관련 섹션을 참고하세요.
+
+## 모드 이름 정하기
+
+새로운 모드를 만들 때는 모드의 이름을 지정해야 해요. 모드 이름은 Warudo에서 모드를 식별하는 중요한 요소에요. "My Mod"나 "Test Mod"처럼 일반적인 이름은 피해주세요. 이런 이름은 다른 사람들이 모드를 찾기 어렵게 만들고, 다른 모드와 충돌할 수 있어요.
+
+고유하고 설명적인 이름을 사용하는 것을 추천해요. 이름에는 공백을 포함할 수 있지만 특수 문자는 사용할 수 없어요.
+
+:::caution
+같은 이름의 모드는 한 번에 하나만 로드될 수 있다는 점을 주의해야 해요. 즉, 같은 모드 폴더를 재사용해서 다른 모드를 내보내면 안 돼요!
+:::
+
+## 핫 리로딩
+
+Warudo는 핫 리로딩을 지원해요. Unity에서 새로운 버전의 모드를 내보내 기존 모드 파일을 덮어쓰면, Warudo가 자동으로 모드를 다시 로드하고 씬에서 변경 사항을 반영합니다. 예를 들어, 큐브의 소재를 빨간색으로 바꾸고 모드를 다시 내보내면, Warudo에서 즉시 큐브가 빨간색으로 변하는 것을 볼 수 있을 거예요!
+
+:::info
+단, 핫 리로딩이 항상 예상대로 작동하지 않을 수 있어요. 문제가 발생하면 Warudo를 다시 시작해보세요.
+:::
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/environment-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/environment-mod.md
new file mode 100644
index 0000000..462c110
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/environment-mod.md
@@ -0,0 +1,97 @@
+---
+sidebar_position: 15
+---
+
+# 환경 모드
+
+어떤 Unity [씬](https://docs.unity3d.com/kr/current/Manual/CreatingScenes.html)이든 Warudo에 환경 모드로 임포트 할 수 있어요!
+
+## 설정
+
+### 1단계: 씬 준비
+
+Unity 씬을 원하는 대로 설정하세요. [Post Processing Stack v2](https://docs.unity3d.com/Packages/com.unity.postprocessing@3.3/manual/index.html)가 지원되니, 씬에 포스트 프로세싱 효과를 추가할 수 있어요.
+
+씬을 최적화하는 것을 추천해요. 예를 들어:
+* 너무 많은 하이폴리곤 모델을 사용하지 마세요;
+* 충돌이 필요하지 않은 모델의 충돌을 비활성화하세요;
+* 스트림에서 보이는 씬의 일부만 필요하다면, 나머지를 비활성화하거나 제거하세요;
+* 실시간 조명 사용을 최소화하고, 가능하면 베이크된 조명을 사용하세요.
+
+### 2단계: 조명 베이크 (선택 사항)
+
+일반적으로 씬에서 조명을 베이크하는 것을 권장해요. 이렇게 하면 환경 모드의 시각적 품질과 성능을 크게 향상시킬 수 있어요.
+
+:::warning
+
+현재 Unity의 기본 렌더 파이프라인(BiRP)에서 **"Standard"** 재질의 베이킹은 지원되지 않아요.
+라이트맵을 베이크해야 하는 경우, 더 나은 결과를 위해 다른 재질을 사용하는 것이 좋습니다.
+
+:::
+
+#### Unity의 기본 라이트매퍼
+
+대부분의 경우 Unity의 기본 라이트매퍼를 사용하는 것으로 충분해요.
+
+Warudo는 방향성 라이트맵을 사용하니 **Windows → Rendering → Lighting**에서 **Directional Mode**가 **Directional**로 설정되어 있는지 확인하세요:
+
+![](/doc-img/en-environment-mod-1.webp)
+
+그렇지 않으면 씬의 조명이 올바르게 적용되지 않을 수 있어요.
+
+:::info
+
+씬에 이미 라이트맵이 베이크되어 있는 경우, 라이트매핑 방향 모드를 전환한 후 **Generate Lighting**을 클릭하여 라이트맵을 다시 베이크해야 합니다.
+
+:::
+
+#### Bakery
+
+[Bakery](https://assetstore.unity.com/packages/tools/level-design/bakery-gpu-lightmapper-122218)라는 서드파티 라이트매퍼를 사용할 수 있어요. 단, 방향성 라이트맵을 베이크해야 합니다. 예를 들어, Bakery에서 [**Directional Mode**를 Dominant Direction 또는 MonoSH](https://geom.io/bakery/wiki/index.php?title=Manual#Directional\_mode)로 설정할 수 있어요.
+
+Bakery가 Warudo에서 제대로 작동하도록 하기 위해서는 아래 단계를 따라야 해요:
+
+1. `Bakery` 폴더를 모드 폴더로 복사하세요 (기본적으로 `Assets` 루트에 있을 거예요);
+2. Bakery 설정에서 **Use scene named output path**를 체크하세요. 이렇게 하면 Bakery 라이트맵이 모드 폴더에 저장돼요. **Directional Mode**는 Dominant Direction 또는 MonoSH처럼 방향성 라이트맵을 생성하는 옵션으로 선택하세요;
+3. **Render**를 클릭해 라이트맵을 렌더링하세요. 반사 프로브도 베이크하려면 **Render reflection probes**를 클릭하세요;
+4. **중요:** `Bakery/_tempScene` 폴더로 이동해 베이크 과정 중 생성된 "_tempScene" Unity 씬 파일을 삭제하세요.
+
+### 3단계: `EnvironmentSettings` 스크립트 추가
+
+익스포트 하고자 하는 Unity 씬을 열고 `EnvironmentSettings` 스크립트(Warudo SDK에 포함됨)를 새 게임 오브젝트 또는 기존 게임 오브젝트에 추가하세요. `Copy from current environment settings` 버튼을 클릭하세요. 이 스크립트는 Unity의 환경 설정(스카이박스, 그림자 색상 등)을 저장해 환경 모드로 익스포트 할 수 있게 합니다.
+
+### 4단계: 씬 이름 변경
+
+씬 이름을 **"Environment"**로 변경하고, 씬을 모드 폴더에 배치하세요 (하위 폴더에 배치 가능).
+
+### 5단계: 모드 익스포트
+
+**Warudo → Build Mod**를 선택하고, 생성된 `.warudo` 파일이 `Environments` 데이터 폴더에 저장되었는지 확인하세요.
+
+## 문제 해결
+
+환경 모드를 만드는 것은 다른 유형의 모드보다 조금 더 까다로울 수 있어요. Warudo에서 환경이 에디터와 다르게 보이는 경우, 아래 체크리스트를 확인해 보세요:
+
+* `EnvironmentSettings` 스크립트를 씬의 게임 오브젝트에 추가했나요?
+* 씬 이름이 **"Environment"**로 되어 있나요?
+* 모든 재질, 셰이더 등이 모드 폴더에 포함되어 있나요?
+* 베이크된 조명을 사용한 경우, 라이트맵과 `LightingSettings` 에셋이 모드 폴더에 포함되어 있나요?
+* 베이크된 조명을 사용한 경우, 모드 폴더에 **하나**의 라이트맵과 `LightingSettings` 에셋만 존재하는지 확인했나요?
+:::caution
+일부 사용자는 여러 세트의 라이트맵과 `LightingSettings` 에셋이 모드 폴더 밖에 존재할 때도 문제가 발생했다고 보고했어요. 이 문제가 지속된다면 하나의 라이트맵과 `LightingSettings` 에셋만 남기고 나머지는 삭제해 보세요.
+:::
+* 베이크된 조명을 사용하는 경우, 라이트맵의 방향 모드가 Directional(또는 Bakery를 사용하는 경우 Dominant Direction/MonoSH)로 설정되어 있나요?
+* 모드 폴더에 여러 개의 Unity 씬이 있나요? 그렇다면 다른 씬을 삭제하세요. (서브폴더 안에 씬이 있을 수 있으니 모든 서브폴더도 확인하세요!)
+* 씬에 적용되지 않은 변경사항이 있는 프리팹이 있나요? 그렇다면 변경사항을 적용하세요. 세로선이 보이는 경우(변경사항이 완전히 적용되지 않았음을 의미함), 해당 프리팹을 우클릭하고 **Prefab → Unpack Completely**를 선택한 후 모드를 다시 익스포트하세요.
+
+![](/doc-img/en-environment-mod-2.webp)
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/mod-sdk.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/mod-sdk.md
new file mode 100644
index 0000000..086bdaf
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/mod-sdk.md
@@ -0,0 +1,71 @@
+---
+sidebar_position: 0
+---
+
+# 개요
+
+![](/doc-img/mod-cover.jpg)
+
+Warudo는 3D 에셋을 직접 Warudo에 가져올 수 있는 완벽한 모딩 시스템을 제공합니다. 이를 통해 다음과 같은 항목을 추가할 수 있어요:
+
+* [캐릭터](character-mod)
+* [캐릭터 애니메이션](character-animation-mod)
+* [소품](prop-mod)
+* [파티클](particle-mod)
+* [환경](environment-mod)
+
+Warudo에서의 모드는 **Warudo SDK**를 사용하여 생성됩니다. SDK는 간단히 말해서 "모드를 만들 수 있도록 도와주는 도구 모음"이며, 이 경우 Unity 프로젝트로 이루어져 있어요! Warudo SDK에서 내보낸 모드는 `.warudo` 파일 확장자를 가지며, 해당 데이터를 Warudo의 폴더에 넣으면 인식됩니다. 여러분은 이 모드 파일을 직접 다른 사람과 공유할 수 있으며 **Discover** 탭을 통해 [Steam Workshop](https://steamcommunity.com/app/2079120/workshop/)에 업로드할 수도 있습니다.
+
+![](/doc-img/en-mod-8.png)
+
Discover 탭에서 모드를 찾아보고 공유하기
+
+## SDK 설정
+
+SDK를 설정하려면 먼저 Unity를 올바르게 설치해야 해요.
+자세한 설치 방법은: [Unity & Warudo SDK 설치](../modding/sdk-installation.md) 가이드를 참고하세요.
+
+## 커스텀 셰이더 {#custom-shaders}
+
+Unity의 기본 렌더링 파이프라인과 호환되는 [lilToon](https://lilxyzw.github.io/lilToon/#/)과 [Poiyomi Shader](https://www.poiyomi.com/) 같은 모든 셰이더를 사용할 수 있어요.
+
+[Warudo Pro](../pro.md)는 [Universal Render Pipeline](https://docs.unity3d.com/Manual/com.unity.render-pipelines.universal.html) (URP)도 지원하여 URP 호환 셰이더도 사용할 수 있어요.
+
+## 커스텀 C# 스크립트 {#custom-scripts}
+
+Warudo 모드를 특별하게 만드는 점은 [C# MonoBehaviour scripts](https://docs.unity3d.com/ScriptReference/MonoBehaviour.html)를 포함할 수 있다는 것이에요. 이를 통해 모드에 상호작용성을 추가할 수 있죠. 예를 들어, 캐릭터 모드에 스크립트를 추가해 캐릭터의 귀를 애니메이션으로 표현하거나, 환경 모드에 스크립트를 추가해 조명을 제어할 수 있어요. Warudo의 내장 컴포넌트와 상호작용하려면 [Scripting](../scripting/overview) 섹션을 참조하세요.
+
+모드 폴더에 배치된 모든 C# 스크립트는 컴파일되어 모드에 포함됩니다. 이러한 스크립트는 일반 Unity 프로젝트에서와 마찬가지로 캐릭터, 소품, 환경에 부착할 수 있어요.
+
+하지만 다음과 같은 제한 사항이 있습니다:
+
+* [Assembly definitions (`.asmdef`)](https://docs.unity3d.com/Manual/ScriptCompilationAssemblyDefinitionFiles.html)는 현재 지원되지 않습니다. assembly definitions에 포함된 C# 스크립트는 모드에 패키지되지 않아요.
+* [ScriptableObjects](https://docs.unity3d.com/ScriptReference/ScriptableObject.html)는 현재 지원되지 않습니다.
+* 컴파일된 DLL은 지원되지 않으며, `.cs` 소스 파일만 포함할 수 있습니다.
+* 모든 `UnityEditor` 네임스페이스를 참조하는 스크립트는 지원되지 않아요.
+
+스크립트를 모드에 포함할 경우, **Log Level**을 All로 설정하고, Mod Settings 창에서 **Clear Console On Build**를 체크 해제할 것을 권장해요:
+
+![](/doc-img/en-mod-13.png)
+
+이것이 빌드 시 더 많은 정보를 제공해 주기 때문에, 콘솔에서 오류를 확인할 수 있어요. 스크립트가 모드 폴더에 배치되었는지 확인했음에도 빌드 로그에 스크립트가 없다는 메시지가 나타나고 컴파일이 생략된 경우, 프로젝트에서 `.csproj` 파일이 제대로 생성되지 않았을 가능성이 있습니다. 이 경우, **Edit → Preferences → External Tools**로 이동해 **Regenerate project files**를 클릭하세요:
+
+![](/doc-img/en-mod-12.png)
+
+그런 다음, 모드를 다시 빌드해 보세요.
+
+## 모딩 프로젝트 백업
+
+모딩 프로젝트를 정기적으로 백업하거나 [Unity Version Control](https://unity.com/solutions/version-control)을 사용하는 것을 권장해요. 특히 모드를 자주 만드는 경우, `Asset/Packages/UMod/ExportSettings.asset` 파일(이 파일은 Unity 에디터에서는 보이지 않아요)을 백업하는 것이 좋아요. 이 파일은 모드 내보내기 설정, 모드 내보내기 디렉터리나 모드 아이콘 같은 정보를 저장해요. 이 파일을 잃어버리면, 모든 모드를 수동으로 다시 만들어야 해요.
+
+:::caution
+모드를 내보낸 후 일부 사용자들이 내보내기 설정을 잃어버리는 문제를 보고한 사례가 있어요. 이 문제는 현재 조사 중이며, 그동안 `ExportSettings.asset` 파일을 정기적으로 백업하는 것을 권장해요.
+:::
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/particle-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/particle-mod.md
new file mode 100644
index 0000000..ce941d1
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/particle-mod.md
@@ -0,0 +1,32 @@
+---
+sidebar_position: 41
+---
+
+# 파티클 모드
+
+파티클 모두는 주로 **Spawn Particle** 같은 노드에서 사용할 수 있는 [파티클 시스템](https://docs.unity3d.com/Manual/PartSysReference.html)이에요.
+
+## 설정
+
+### 1단계: 모델 준비
+
+파티클 게임 오브젝트를 씬에 배치하고 원하는 위치와 회전으로 조정하세요. 마우스 오른쪽 버튼을 클릭하고 **Create Empty Parent**를 선택해 빈 게임 오브젝트를 소품의 루트로 생성하세요.
+
+플레이 모드로 전환하고 부모 게임 오브젝트를 이동시켜 파티클 시스템이 예상대로 파티클을 방출하는지 확인하세요. 그렇지 않다면 파티클 시스템의 설정을 조정해야 할 수도 있어요. 예를 들어 **Simulation Space**를 World 대신 Local로 설정할 수 있어요.
+
+### 2단계: 프리팹 생성
+
+루트 파티클 게임 오브젝트를 선택하고 모드 폴더로 드래그해 프리팹을 생성하세요. 프리팹 이름을 **"Particle"**로 지정하고, 반드시 모드 폴더(하위 폴더도 가능)에 배치하세요.
+
+### 3단계: 모드 익스포트
+
+**Warudo → Build Mod**를 선택하고 생성된 `.warudo` 파일이 `Particles` 데이터 폴더에 저장되었는지 확인하세요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/prop-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/prop-mod.md
new file mode 100644
index 0000000..b73e0f1
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/prop-mod.md
@@ -0,0 +1,40 @@
+---
+sidebar_position: 40
+---
+
+# 소품 모드
+
+소품 모드는 의자나 검과 같은 Unity [프리팹](https://docs.unity3d.com/kr/current/Manual/Prefabs.html)을 Warudo에서 소품으로 사용하는 모드를 의미해요.
+
+## 설정
+
+### 1단계: 모델 준비
+
+소품 모델을 모딩 프로젝트에 임포트하세요. 씬에 소품을 배치한 후, 원하는 위치와 회전으로 조정하세요. 소품을 우클릭하고 **Create Empty Parent**를 선택해 빈 게임 오브젝트를 소품의 루트로 만드세요.
+
+:::tip
+빈 부모(empty parent)를 생성하는 이유는 소품 에셋이 루트 게임 오브젝트의 변환을 소품의 변환으로 사용하기 때문이에요. 만약 빈 부모(empty parent)를 생성하지 않으면 소품의 변환에 적용한 변경사항이 유지되지 않아요.
+:::
+
+### 2단계: 캐릭터 부착 설정 (선택 사항)
+
+소품이 캐릭터에 부착되도록 의도된 경우(예: 액세서리), `CharacterAttachmentSettings` 스크립트를 프리팹에 추가해 기본으로 부착될 캐릭터의 뼈대를 지정할 수 있어요. 이렇게 하면 사용자가 Prop → Transform Attachment → Attach To에서 캐릭터를 선택할 때, 소품이 기본 뼈대(머리) 대신 지정된 캐릭터 뼈대에 자동으로 부착돼요.
+
+또한, 소품의 자식 변환에 `CharacterAttachmentRotationConstraint` 스크립트를 추가해 소품의 뼈대가 특정 캐릭터의 뼈대와 함께 회전하도록 설정할 수 있어요. 예를 들어, 장갑 소품의 자식 변환은 캐릭터의 손가락 뼈대와 함께 회전해야 해요.
+
+### 3단계: 프리팹 생성
+
+소품의 루트 게임 오브젝트를 선택하고 모드 폴더로 드래그해 프리팹을 생성하세요. 프리팹 이름을 **"Prop"**으로 지정하고, 반드시 모드 폴더(하위 폴더도 가능)에 배치하세요.
+
+### 4단계: 모드 익스포트
+
+**Warudo → Build Mod**를 선택하고 생성된 `.warudo` 파일이 `Props` 데이터 폴더에 저장되었는지 확인하세요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/sdk-installation.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/sdk-installation.md
new file mode 100644
index 0000000..323363c
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/sdk-installation.md
@@ -0,0 +1,203 @@
+---
+sidebar_position: 1
+---
+
+# Unity & Warudo SDK 설치
+
+이 페이지에서는 **Unity Editor**와 **Warudo SDK**를 단계별로 설치하는 방법을 안내할거에요.
+
+---
+
+:::info
+
+현재 **64-bit Windows** 시스템만 지원돼요.
+
+:::
+
+## 1단계 - Unity Hub 설치
+
+먼저, **Unity Hub**를 다운로드해야 해요. (보통 최신 버전이면 괜찮아요.)
+Unity Hub는 서로 다른 버전의 Unity Editor와 그 버전의 프로젝트를 관리하는 데 사용하는 소프트웨어예요.
+
+[**Unity 공식 웹사이트**](https://unity.com/download)를 열고 아래 이미지의 빨간 상자 중 하나를 클릭해서 Unity Hub를 다운로드하세요. 다운로드를 진행하려면 Unity 계정이 필요할 수 있어요.
+
+![](/doc-img/sdk-installation-13.png)
+
+다운로드한 UnityHubSetup.exe 파일을 실행해 원하는 경로에 설치하세요.
+설치 후, Unity Hub가 정상적으로 열리는지 확인해 주세요.
+
+![](/doc-img/sdk-installation-2.png)
+
+로그인 후, Unity Hub는 자동으로 최신 장기 지원(LTS) 버전의 Unity Editor를 다운로드하라고 제안해요.
+하지만 Warudo는 이 버전을 사용하지 않으니, **Skip installation**를 클릭하는 것이 좋습니다.
+
+만약 해당 버전을 설치하고 싶다면, 설치 경로를 `...\` (예를 들어, `D:\Softwares\Unity\2022.3.17f1`)로 변경하는 것을 강력히 권장해요.
+이렇게 하면 **여러 버전**의 Unity Editor를 더 편리하게 관리할 수 있고, 기본 설치 경로인 `C:\Program Files\Unity\Hub\Editor`를 사용하지 않게 됩니다.
+
+![](/doc-img/sdk-installation-3.png)
+
+Unity의 개인용 에디션 사용 조건을 충족하는 경우, 이 단계에서 개인 라이선스를 활성화하고 받을 수 있어요.
+
+![](/doc-img/sdk-installation-4.png)
+
+이전 단계에서 활성화를 건너뛰었거나 활성화가 실패한 경우, 나중에 Unity Hub에서 라이선스를 수동으로 활성화할 수도 있어요.
+
+![](/doc-img/sdk-installation-5.png)
+
+![](/doc-img/sdk-installation-6.png)
+
+![](/doc-img/sdk-installation-7.png)
+
+Unity HubUnity Hub의 언어는 【Preferences】-【Appearance】에서 변경할 수 있어요.
+
+![](/doc-img/sdk-installation-8.png)
+
+## 2단계 - Unity Editor 설치
+
+다음으로 Unity Editor 설치 프로그램을 다운로드해야 하는데, 반드시 **Unity 2021.3.18f1** 버전 이어야 해요.
+
+이 버전은 Unity Hub에서 직접 다운로드할 수 없으므로, 다음과 같은 절차를 따르세요:
+
+1. [Unity Download Archive](https://unity.com/releases/editor/archive)로 이동합니다;
+2. **【Unity 2021.X】** 탭에서 【Unity **2021.3.18** (February 1, 2023)】을 찾습니다;
+3. 【Downloads (Win)】을 클릭합니다;
+4. **【Unity Editor (64-bit)】**를 선택합니다;
+5. 【 `UnitySetup 64-2021.3.18f1.exe` 】가 다운로드 완료될 때까지 기다립니다. (약 2.3 GiB 크기);
+
+![](/doc-img/sdk-installation-9.png)
+
+다음으로 `UnitySetup 64-2021.3.18f1.exe` 파일을 실행해 설치를 시작하세요
+
+기본 설치 위치는 `C:\Program Files\Unity 2021.3.18f1`이며, 설치 경로는 변경할 수 있지만 버전 충돌을 피하기 위해 경로에 **버전 번호를 포함**하는 것이 좋습니다.
+
+설치 과정은 다소 시간이 걸리니 인내심을 가지고 기다리세요.
+
+설치가 완료 된 후 다음 단계를 진행하세요:
+
+1. Unity Hub를 엽니다;
+2. 【Installs】 탭에서 【Locate】을 클릭합니다;
+3. 설치 경로에서 【Editor】 하위 폴더를 찾아 【Unity.exe】 파일을 선택합니다;
+4. 선택 후 【Select Editor】를 클릭합니다.
+
+이제 Unity Hub에서 `2021.3.18f1` 버전의 Unity.exe가 정상적으로 표시될 거예요.
+
+![](/doc-img/sdk-installation-10.png)
+
+## 3단계 - Warudo SDK 다운로드 및 임포트
+
+Unity에 Warudo SDK를 설정하는 방법에는 두 가지가 있어요:
+
+- 모드 프로젝트 템플릿을 다운로드하는 방법,
+- SDK `.unitypackage` 파일을 프로젝트에 수동으로 임포트하는 방법
+
+첫 번째 옵션을 권장해요. 이 방법이 더 쉽고 오류가 발생할 가능성이 적어요.
+두 번째 옵션은 기존 모딩 프로젝트를 업데이트할 때 더 적합해요.
+
+### 옵션 1: 모드 프로젝트 템플릿 (추천)
+
+[Warudo SDK 0.12.0 Modding Project.zip](https://docs.warudo.app/sdk/Warudo%20SDK%200.12.0%20Modding%20Project.zip)
+
+1. 위의 zip 파일을 **다운로드**한 후, 내용을 원하는 위치에 **압축 해제**하세요. 예를 들어, `D:\Softwares\Unity\2022.3.17f1\Projects\WarudoModding` 같은 경로로 해제할 수 있어요.
+2. Unity Hub에서 **"Add"** 버튼을 클릭하고, 위에서 선택한 폴더를 선택하세요.
+3. 그 후 Unity가 프로젝트를 실행할 때까지 기다리세요. 프로젝트를 처음 열 때 Unity가 프로젝트를 설정하는 데 5-10분 정도 걸릴 수 있어요.
+
+![](/doc-img/en-mod-9.png)
+
+콘솔에서 오류가 있는지 확인하세요. 몇 가지 경고가 표시될 수 있는데, 이는 정상이에요:
+
+![](/doc-img/en-mod-10.png)
+
+오류가 없다면 준비 완료! 이제 [당신의 첫 모드를 만들어보세요](creating-your-first-mod).
+
+:::tip
+
+콘솔에 오류가 보인다면 "Clear" 버튼을 클릭해 오류가 사라지는지 확인해 보세요. 그래도 문제가 해결되지 않으면, [Discord](https://discord.gg/warudo)에서 저희에게 문의하세요.
+
+:::
+
+### 옵션 2: 수동 설치 (권장하지 않음)
+
+:::caution
+
+**기존 모딩 프로젝트를 업데이트**하거나, 첫 번째 옵션이 작동하지 않는 매우 드문 경우에만 이 옵션을 사용하세요.
+
+:::
+
+Unity 프로젝트를 연 후, 프로젝트 폴더 아래 `Packages/manifest.json`파일을 열고(이 파일은 Unity 에디터에서 보이지 않아요) `dependencies` 섹션에 다음 **9개** dependencies를 추가하세요:
+
+```
+{
+ "dependencies": {
+ "com.unity.burst": "1.7.2",
+ "com.unity.cinemachine": "2.8.9",
+ "com.unity.collections": "1.4.0",
+ "com.unity.postprocessing": "3.2.2",
+ "com.unity.nuget.newtonsoft-json": "3.0.2",
+ "com.vrmc.gltf": "https://github.com/vrm-c/UniVRM.git?path=/Assets/UniGLTF#v0.107.0",
+ "com.vrmc.univrm": "https://github.com/vrm-c/UniVRM.git?path=/Assets/VRM#v0.107.0",
+ "com.vrmc.vrmshaders": "https://github.com/vrm-c/UniVRM.git?path=/Assets/VRMShaders#v0.107.0",
+ "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask",
+ // ...
+```
+
+Unity로 돌아가 프로젝트가 다시 로드될 때까지 기다리고, 오류가 없는지 확인하세요.
+
+:::caution
+
+만약 `An error occurred while resolving packages / Error adding package`라는 메시지와 함께 `No 'git' executable was found. Please install Git on your system then restart Unity and Unity Hub`와 비슷한 오류 메시지가 나타난다면, 시스템에 Git이 설치되어 있지 않은 것입니다.
+
+![](/doc-img/en-mod-sdk-1.webp)
+
+이 문제를 해결하려면 [https://git-scm.com/download](https://git-scm.com/download)에서 Git을 다운로드한 후 Unity와 Unity Hub를 재시작하세요.
+
+:::
+
+**File → Build Settings... → Player Settings... → Other Settings**에서 **Api Compatibility Level**이 .NET Framework로 설정되어 있는지 확인하세요.
+
+![](/doc-img/en-mod-sdk-2.webp)
+
+아래의 `.unitypackage`를 다운로드하여 Unity 프로젝트에 임포트하세요:
+
+
+
+
+
+:::caution
+
+**기존** 프로젝트에 임포트하는 경우, 다음 항목 중 설치된 것이 있다면:
+
+* [Animancer](https://assetstore.unity.com/packages/tools/animation/animancer-pro-116514)
+* [Dynamic Bones](https://assetstore.unity.com/packages/tools/animation/dynamic-bone-16743)
+* [Final IK](https://assetstore.unity.com/packages/tools/animation/final-ik-14290)
+* [Magica Cloth](https://assetstore.unity.com/packages/tools/physics/magica-cloth-160144)
+* [PuppetMaster](https://assetstore.unity.com/packages/tools/physics/puppetmaster-48977)
+
+해당 폴더를 임포트에서 체크 해제하세요. 그렇지 않으면 컴포넌트가 스텁 파일로 대체됩니다::
+
+* `Plugins/Animancer`
+* `Packages/DynamicBone`
+* `Plugins/RootMotion/FinalIK`
+* `Plugins/MagicaCloth`
+* `Plugins/RootMotion/PuppetMaster`
+
+:::
+
+콘솔에서 오류가 있는지 확인하세요. 오류가 없다면, 이제 [당신의 첫 모드를 만들어보세요](creating-your-first-mod).
+
+:::tip
+
+콘솔에 오류가 보인다면 "Clear" 버튼을 클릭해 오류가 사라지는지 확인해 보세요. 그래도 문제가 해결되지 않으면, [Discord](https://discord.gg/warudo)에서 저희에게 문의하세요.
+
+:::
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/modding/sharing.md b/i18n/ko/docusaurus-plugin-content-docs/current/modding/sharing.md
new file mode 100644
index 0000000..1e85f9b
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/modding/sharing.md
@@ -0,0 +1,32 @@
+---
+sidebar_position: 100
+---
+
+# 모드 공유하기
+
+모드를 만들었다면 [Steam Workshop](https://steamcommunity.com/app/2079120/workshop/)에 업로드해 다른 사람들과 공유할 수 있어요. 몇 번의 클릭만으로 가능해요!
+
+## 튜토리얼
+
+**Discover**탭으로 이동한 후 **Published Items → Create Item**을 클릭하세요:
+
+![](/doc-img/en-sharing-mod-1.png)
+
+팝업 창에서 업로드 폼을 작성하세요:
+
+![](/doc-img/en-sharing-mod-2.png)
+
+**Files** 목록에서 **+** 버튼을 클릭해 업로드할 파일을 선택할 수 있어요.
+
+![](/doc-img/en-sharing-mod-3.png)
+
+모든 준비가 완료되면 **OK**를 클릭하고 업로드가 완료될 때까지 기다리면 돼요. 쉽죠?
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/assets.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/assets.md
new file mode 100644
index 0000000..0b9147e
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/assets.md
@@ -0,0 +1,174 @@
+---
+sidebar_position: 40
+---
+
+# 에셋
+
+에셋은 씬에서 기능이나 동작을 구현하는 독립적인 오브젝트에요. 노드와는 다르게, 에셋은 보통 더 복잡한 로직을 캡슐화하고 있어요. 프로그램에서 클래스처럼 생각할 수 있으며, Unity의 `MonoBehaviour`와 유사해요.
+
+## 타입 정의
+
+에셋 타입을 생성하고 씬에 인스턴스화하여 저장할 수 있어요. 에셋 타입은 `Asset` 타입을 상속받고 `[AssetType]` 속성으로 정의돼요:
+
+```csharp
+[AssetType(
+ Id = "c6500f41-45be-4cbe-9a13-37b5ff60d057",
+ Title = "Hello World",
+ Category = "CATEGORY_DEBUG",
+ Singleton = false
+)]
+public class HelloWorldAsset : Asset {
+ // Asset implementation
+}
+```
+
+파라미터 요약:
+
+- **`Id`**: 에셋 타입을 위한 고유 식별자; 각 새로운 에셋 타입에 대해 [새로운 GUID를 생성](https://www.guidgenerator.com/online-guid-generator.aspx)해야 해요. 이는 에셋 인스턴스의 UUID (`asset.Id`)와는 다르다는 점에 유의하세요..
+- **`Title`**: 추가 에셋 메뉴에 표시될 에셋 타입의 이름이에요.
+- **`Category`**: 선택사항. *Add Asset* 메뉴에서 에셋이 속할 그룹이에요.
+- **`Singleton`**: 선택사항. `true`로 설정하면 장면에 해당 에셋 인스턴스가 하나만 존재할 수 있어요. 기본값은 `false`에요.
+
+:::info
+다음은 사용할 수 있는 일반적인 에셋 카테고리입니다: `CATEGORY_INPUT`, `CATEGORY_CHARACTERS`, `CATEGORY_PROP`, `CATEGORY_ACCESSORY`, `CATEGORY_ENVIRONMENT`, `CATEGORY_CINEMATOGRAPHY`, `CATEGORY_EXTERNAL_INTERACTION`, `CATEGORY_MOTION_CAPTURE`.
+:::
+
+## 컴포넌트
+
+에셋 타입은 데이터 인풋과 트리거를 정의할 수 있어요. 노드와는 달리, 에셋에는 데이터 아웃풋이나 플로우 인풋/아웃풋이 없어요.
+
+![](/doc-img/en-scripting-concepts-4.png)
+
+## 라이프사이클
+
+에셋은 [엔티티](entities#lifecycle) 페이지에서 설명된 라이프 사이클을 갖고 있어요. 이러한 메서드를 오버라이드하여 다양한 작업을 수행할 수 있어요. 예를 들어 `OnUpdate()` 메서드는 매 프레임 호출되며, Unity의 `Update()` 메서드와 비슷해요.
+
+## 활성 상태 {#active-state}
+
+노드와는 달리, 에셋은 "활성" 상태, 즉 사용할 준비가 되었는지를 나타내는 활성 상태를 가지고 있어요. 예를 들어, 캐릭터 에셋에 `Source`가 선택되지 않은 경우, 에디터에서 비활성 상태로 표시돼요.
+
+![](/doc-img/en-custom-asset-1.png)
+
+기본적으로 에셋은 생성될 때 활성 상태가 **아니에요**. `SetActive(bool state)` 메서드를 호출해 에셋의 활성 상태를 설정할 수 있어요. 예를 들어, 에셋이 항상 사용할 준비가 되어 있다면 `OnCreate` 메서드에서 이를 활성으로 설정할 수 있어요:
+
+```csharp
+public override void OnCreate() {
+ base.OnCreate();
+ SetActive(true);
+}
+```
+
+에셋이 외부 서버(예: 원격 추적 장치)에 연결될 때만 동작하는 경우, 연결이 성공적으로 이루어졌을 때에만 활성으로 설정할 수 있어요:
+
+```csharp
+[DataInput]
+public string RemoteIP = "127.0.0.1";
+
+[DataInput]
+public string RemotePort = "12345";
+
+public override void OnCreate() {
+ base.OnCreate();
+ WatchAll(new [] { nameof(RemoteIP), nameof(RemotePort) }, ResetConnection); // When RemoteIP or RemotePort changes, reset the connection
+}
+
+protected void ResetConnection() {
+ SetActive(false); // Inactive until connection is established
+ if (ConnectToRemoteServer(RemoteIP, RemotePort)) {
+ SetActive(true);
+ }
+}
+```
+
+:::tip
+에셋이 "사용 준비 완료" 상태인지 여부를 결정하는 것은 전적으로 개발자의 판단에 달려 있어요. Warudo의 내부 에셋에서 사용되는 규칙은 에셋이 제대로 작동하기 위해 필요한 모든 데이터 인풋이 설정되었을 때 활성 상태가 된다는 것이에요.
+:::
+
+## 게임오브젝트 생성
+
+원할 때마다 (Unity) 씬에서 게임 오브젝트를 생성할 수 있어요. 예를 들어, 다음 에셋은 에셋이 생성될 때 큐브 게임 오브젝트를 만들고, 에셋이 파괴될 때 이를 삭제해요:
+
+```csharp
+private GameObject gameObject;
+
+public override void OnCreate() {
+ base.OnCreate();
+ gameObject = GameObject.CreatePrimitive(PrimitiveType.Cube);
+}
+
+public override void OnDestroy() {
+ base.OnDestroy();
+ Object.Destroy(gameObject);
+}
+```
+
+하지만 사용자는 이 큐브를 움직일 수 없어요. 왜냐하면 큐브를 제어할 수 있는 데이터 인풋이 없기 때문이에요! 큐브의 위치, 크기 등을 제어할 수 있는 데이터 인풋을 추가할 수 있지만, 더 쉬운 방법은 `GameObjectAsset` 타입을 상속받는 것이에요:
+
+```csharp
+using UnityEngine;
+using Warudo.Core.Attributes;
+using Warudo.Plugins.Core.Assets;
+
+[AssetType(
+ Id = "4c00b14a-aed5-423e-abe6-6921032439c5",
+ Title = "My Awesome Cube",
+ Category = "CATEGORY_DEBUG"
+)]
+public class MyAwesomeCubeAsset : GameObjectAsset {
+ protected override GameObject CreateGameObject() {
+ return GameObject.CreatePrimitive(PrimitiveType.Cube);
+ }
+}
+```
+
+`GameObjectAsset`은 게임 오브젝트의 생성과 삭제를 자동으로 처리해주며, 사용자에게 게임 오브젝트의 위치, 회전, 크기를 제어할 수 있는 `Transform` 데이터 인풋을 제공해요:
+
+![](/doc-img/en-custom-asset-2.png)
+
+:::tip
+`GameObjectAsset`을 언제 사용해야 할까요? 에셋이 "(Unity) 씬에서 사용자가 움직일 수 있는 무언가"라면 `GameObjectAsset`을 상속받는 것이 좋은 선택이에요.
+:::
+
+## 이벤트
+
+`Asset` 타입은 다음 이벤트를 호출하며, 이를 통해 이벤트를 수신할 수 있어요:
+
+- **`OnActiveStateChange`**: 에셋의 활성 상태가 변경될 때 호출돼요.
+- **`OnSelectedStateChange`**: 에셋이 에디터에서 선택되거나 선택 해제될 때 호출돼요.
+- **`OnNameChange`**: 에셋의 이름이 변경될 때 호출돼요.
+
+예를 들어, 내장된 [Leap Motion tracking](../../mocap/leap-motion) 에셋은 `OnSelectedStateChange` 이벤트를 수신하여 에셋이 선택되면 (Unity) 씬에서 Leap Motion 컨트롤러 모델을 표시해요.
+
+```csharp
+public override void OnCreate() {
+ base.OnCreate();
+ OnSelectedStateChange.AddListener(selected => {
+ if (selected) {
+ // Show the model
+ } else {
+ // Hide the model
+ }
+ });
+}
+```
+
+## 코드 예시
+
+### 기본
+
+- [AnchorAsset.cs](https://gist.github.com/TigerHix/c549e984df0be34cfd6f8f50e741aab2)
+Attachable / GameObjectAsset 예시
+
+### 고급
+
+- [CharacterPoserAsset.cs](https://gist.github.com/TigerHix/8413f8e10e508f37bb946d8802ee4e0b)
+IK 앵커로 캐릭터 포즈를 조절하는 커스텀 에셋.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/entities.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/entities.md
new file mode 100644
index 0000000..4156c10
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/entities.md
@@ -0,0 +1,67 @@
+---
+sidebar_position: 2
+---
+
+# 엔티티
+
+엔티티는 [씬](scene) 내에서 지속되는 오브젝트(예: 노드, 에셋) 또는 씬 간에 지속되는 오브젝트(예: 플러그인)를 의미해요. Warudo에서 스크립팅은 기본적으로 자신만의 엔티티 유형을 정의하고, Warudo 런타임과 상호작용하는 것을 말해요. Warudo에서 제공하는 기본 클래스를 상속받아 자신의 엔티티 유형을 정의할 수 있어요:
+
+- **노드:** `Node`를 상속받아 새로운 노드 유형을 만들 수 있어요. 노드 유형은 데이터 인풋, 데이터 아웃풋, 플로우 인풋, 플로우 아웃풋, 트리거를 정의할 수 있어요. 노드는 노드 팔레트에서 blueprint 에디터로 드래그하여 인스턴스화되며, 노드 인스턴스는 blueprint와 함께 저장돼요.
+- **에셋:** `Asset`을 상속받아 새로운 에셋 유형을 만들 수 있어요. 에셋 유형은 데이터 인풋과 트리거를 정의할 수 있어요. 에셋은 *Add Asset* 메뉴에서 인스턴스화되며, 에셋 인스턴스는 씬과 함께 저장돼요.
+- **플러그인:** `Plugin`을 상속받아 새로운 플러그인 유형을 만들 수 있어요. 플러그인 유형은 데이터 인풋과 트리거를 정의할 수 있어요. 플러그인은 Warudo가 시작되거나 핫 리로딩될 때 로드돼요. 플러그인 유형당 인스턴스는 하나뿐이며, 데이터 인풋은 씬 간에도 저장돼요.
+- **구조화된 데이터:** `StructuredData`를 상속받아 새로운 [구조화된 데이터](structured-data) 유형을 만들 수 있어요. 구조화된 데이터는 노드, 에셋, 또는 플러그인의 '내장된' 데이터 인풋으로 사용할 수 있는 복합 데이터 유형이에요.
+
+:::info
+[당신의 첫 스크립트를 만들어보세요](../creating-your-first-script.md) 튜토리얼에서, 우리는 `HelloWorldNode`라는 커스텀 노드 유형과 `CookieClickerAsset`이라는 커스텀 에셋 유형을 만들었어요..
+:::
+
+:::tip
+엔티티 개념은 Unity의 MonoBehaviour와 유사하지만, 더 구조화된 방식이에요.
+:::
+
+엔티티가 인스턴스화되면, Warudo는 자동으로 UUID(고유 식별자)를 엔티티에 할당해요. 이 UUID는 `Id` 속성을 통해 접근할 수 있으며, 씬 내에서 엔티티를 식별하는 데 사용되고, 씬과 함께 저장돼요.
+
+엔티티 유형에는 일반 C# 필드와 메서드를 포함할 수 있지만, Warudo 런타임과 인터페이스하려면 엔티티 내에서 [**포트와 트리거**](ports-and-triggers)를 만들어야 해요. 모든 엔티티는 [데이터 인풋 포트](ports-and-triggers#data-input-ports)와 [트리거](ports-and-triggers#triggers)를 가질 수 있으며, 노드는 추가로 [데이터 아웃풋 포트](ports-and-triggers#data-output-ports), [플로우 인풋 포트](ports-and-triggers#flow-input-ports) 그리고 [플로우 아웃풋 포트](ports-and-triggers#flow-output-ports)를 가질 수 있어요.
+
+![](/doc-img/en-custom-node-1.png)
+
+![](/doc-img/en-scripting-concepts-4.png)
+
+## 라이프사이클 {#lifecycle}
+
+엔티티는 여러 단계로 구성된 라이프사이클을 가지고 있어요:
+
+- **`OnCreate()`:** 엔티티가 생성될 때 호출돼요. 이곳에서 인스턴스 필드를 초기화하거나, 이벤트를 구독하는 등의 작업을 해야 해요.
+ - **에셋:** 사용자가 씬에 새로운 에셋을 추가하거나, 씬이 로드될 때 호출돼요.
+ - **노드:** 사용자가 blueprint에 새로운 노드를 추가하거나, 씬이 로드될 때 호출돼요. 노드는 항상 에셋이 생성된 후에 생성돼요.
+ - **플러그인:** Warudo가 시작되거나 핫 리로딩될 때 플러그인이 로드되면 호출돼요.
+ - **구조화된 데이터:** 부모 엔티티가 생성될 때나 사용자가 구조화된 데이터 배열에 새 데이터를 추가할 때 호출돼요.
+- **`OnDestroy()`:** 엔티티가 파괴될 때 호출돼요. 여기서 리소스를 해제하는 등의 작업을 해야 해요.
+ - **에셋:** 사용자가 씬에서 에셋을 제거하거나 씬이 언로드될 때 호출돼요.
+ - **노드:** 사용자가 blueprint에서 노드를 제거하거나 씬이 언로드될 때 호출돼요. 노드는 항상 에셋이 파괴된 후에 파괴돼요.
+- **`Deserialize(TSerialized data)`:** 엔티티가 저장된 상태에서 역직렬화될 때 호출돼요. 예를 들어, 씬이 처음 열리고 로드될 때 에셋에서 이 메서드가 호출돼요. 보통 이 메서드를 오버라이드할 필요는 없어요.
+- **`Serialize()`:** 엔티티가 저장 상태로 직렬화되어야 할 때 호출돼요. 예를 들어, 씬이 저장되거나 편집기로 보내져야 할 때 이 메서드가 호출돼요. 이 메서드 역시 보통 오버라이드할 필요는 없어요.
+
+에셋, 노드, 플러그인은 추가적인 라이프사이클 단계를 가지고 있어요:
+
+- **`OnUpdate()`:** 엔티티가 파괴되지 않은 동안 매 프레임마다 호출돼요. Unity의 `Update()` 메서드와 유사해요.
+ - `OnPreUpdate()`와 `OnPostUpdate()`도 제공돼요. 각각 `OnUpdate()` 전에 또는 후에 호출돼요.
+- **`OnLateUpdate()`:** 매 프레임의 `OnUpdate()`후에 호출돼요. Unity의 `LateUpdate()` 메서드와 유사해요.
+- **`OnFixedUpdate()`:** 고정된 프레임마다 호출돼요. Unity의 `FixedUpdate()` 메서드와 유사해요.
+- **`OnEndOfFrame()`:** 프레임이 끝날 때 호출돼요. Unity의 [`WaitForEndOfFrame` 코루틴](https://docs.unity3d.com/ScriptReference/WaitForEndOfFrame.html)과 유사해요.
+- **`OnDrawGizmos()`:** 엔티티가 기즈모를 그려야 할 때 호출돼요. Unity의 `OnDrawGizmos()` 메서드와 유사해요.
+
+:::info
+업데이트 순서는 다음과 같아요: 플러그인 → 에셋 → 노드.
+:::
+
+이 라이프사이클 메서드를 오버라이드하여 엔티티의 사용자 정의 동작을 구현할 수 있어요. 예를 들어, 매 프레임마다 어떤 작업을 실행하려면 `OnUpdate()` 메서드를 오버라이드하면 돼요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/events.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/events.md
new file mode 100644
index 0000000..18a6f45
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/events.md
@@ -0,0 +1,62 @@
+---
+sidebar_position: 90
+---
+
+# 글로벌 이벤트 {#events}
+
+Warudo에는 글로벌 이벤트를 구독하고 브로드캐스트할 수 있는 `EventBus` 클래스가 있어요. 커스텀 이벤트 클래스를 정의하려면 `Warudo.Core.Events.Event` 클래스를 상속하세요. 예를 들어:
+
+```csharp
+public class MyEvent : Event {
+ public string Message { get; }
+ public MyEvent(string message) {
+ Message = message;
+ }
+}
+```
+
+이벤트를 구독하려면:
+
+```csharp
+Context.EventBus.Subscribe(e => {
+ Debug.Log(e.Message);
+});
+```
+
+이벤트를 브로드캐스트하려면:
+
+```csharp
+Context.EventBus.Broadcast(new MyEvent("Hello, world!"));
+```
+
+이벤트를 더 이상 듣고 싶지 않을 때는 구독을 해제해야 해요:
+
+```csharp
+var subscriptionId = Context.EventBus.Subscribe(e => {
+ Debug.Log(e.Message);
+});
+// Later
+Context.EventBus.Unsubscribe(subscriptionId);
+```
+
+### 엔티티에서 이벤트 구독
+
+엔티티 타입 내에서 코드를 작성하는 경우, 구독 ID를 처리하지 않고 직접 `Subscribe` 메서드를 사용할 수 있어요:
+
+```csharp
+// Inside an entity type, i.e., asset, node, plugin, structured data
+Subscribe(e => {
+ Debug.Log(e.Message);
+});
+```
+
+이벤트는 엔티티가 삭제될 때 자동으로 구독 해제돼요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/generating-blueprints.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/generating-blueprints.md
new file mode 100644
index 0000000..81e7ab9
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/generating-blueprints.md
@@ -0,0 +1,70 @@
+---
+sidebar_position: 130
+---
+
+# Blueprint 생성
+
+Warudo에서 [추적](../../mocap/overview)을 설정할 때, 추적 blueprint가 생성되는 것을 볼 수 있을 거예요. Blueprint를 생성하는 것은 사용자에게 blueprint를 수정할 유연성을 제공하면서도, 모든 로직을 커스텀 에셋이나 플러그인에 하드코딩하지 않고 유용하게 사용할 수 있습니다.
+
+## 코드 사용
+
+Blueprint를 생성하는 권장 방법은 코드를 사용하는 것이에요. 이렇게 하면 노드를 조건부로 추가하거나 제거하는 등 생성 과정을 제어할 수 있고, 노드 타입을 올바르게 참조하는 것을 보장할 수 있습니다. 단점은 코드를 작성하는 것이 더 번거로울 수 있다는 점이에요.
+
+다음은 두 개의 노드를 포함한 blueprint를 생성하는 최소한의 예제입니다: `On Update`와 `Show Toast`. `On Update` 노드는 `Show Toast` 노드에 연결되어 있어 매 프레임마다 토스트 메시지가 표시됩니다.
+
+```csharp
+var graph = new Graph {
+ Name = "My Awesome Blueprint",
+ Enabled = true // Enable the blueprint by default
+};
+
+// Add two nodes: On Update and Show Toast
+var onUpdateNode = graph.AddNode();
+var toastNode = graph.AddNode();
+toastNode.Header = "Hey!";
+toastNode.Caption = "This is a toast message!";
+
+// Connect the nodes so that the toast is shown each frame
+graph.AddFlowConnection(onUpdateNode, nameof(onUpdateNode.Exit), toastNode, nameof(toastNode.Enter));
+
+// Add the graph to the opened scene
+Context.OpenedScene.AddGraph(graph);
+
+// Send the updated scene to the editor
+Context.Service.BroadcastOpenedScene();
+```
+
+:::tip
+에디터는 모든 노드가 (0, 0) 위치에 있을 때 blueprint를 자동으로 포맷해 줍니다. 따라서 노드의 정확한 위치를 걱정할 필요는 없어요. 그래도 노드의 위치에 접근하고 싶다면, `node.GraphPosition`을 사용할 수 있습니다..
+:::
+
+:::info
+모든 내장 노드 타입이 Warudo SDK에 포함된 것은 아닙니다. 플러그인 모드를 생성하고 SDK에 포함되지 않은 노드 타입을 참조하려면 `AddNode(string nodeTypeId)` 메서드를 사용해 타입 ID로 노드를 생성할 수 있습니다.
+:::
+
+## JSON 사용
+
+더 간단하지만 비추천하는 방법은 에디터에 내장된 "Import Blueprint From JSON" 기능을 사용하는 것이에요. 에디터에서 blueprint를 생성한 다음, JSON으로 내보내고 플러그인에 저장한 후, `Service` 클래스를 사용해 JSON 파일을 blueprint로 가져올 수 있습니다.
+
+```csharp
+var fileContents = "..."; // Assume this is the JSON file contents
+Context.Service.ImportGraph(fileContents); // Import the blueprint
+Context.Service.BroadcastOpenedScene(); // Send the updated scene to the editor
+```
+
+그 후에는 이름을 통해 생성된 blueprint에 접근할 수 있습니다.
+
+```csharp
+var graph = Context.OpenedScene.GetGraphs().Values.First(it => it.Name == "My Awesome Blueprint");
+```
+
+이 방법은 다소 비효율적이고, 프로덕션 코드에 사용하기에는 적합하지 않아요. JSON 형식은 향후 버전에서 변경될 수 있고, 코드를 통해 blueprint를 생성하는 것만큼 유연하지 않기 때문입니다.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/io.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/io.md
new file mode 100644
index 0000000..bbf11db
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/io.md
@@ -0,0 +1,63 @@
+---
+sidebar_position: 110
+---
+
+# 데이터 저장 및 불러오기 {#io}
+
+## 데이터 인풋 사용
+
+엔티티의 [데이터 인풋](ports-and-triggers#data-inputs)은 자동으로 직렬화됩니다. 즉, 커스텀 에셋이나 노드에 데이터를 저장해야 할 경우, 가장 좋은 방법은 단순히 데이터 인풋을 정의하는 것입니다:
+
+```csharp
+[DataInput]
+public Vector3 MySpecialVector3;
+```
+
+사용자에게 표시하고 싶지 않은 경우 숨겨진 데이터 인풋도 사용할 수 있습니다:
+
+```csharp
+[DataInput]
+[Hidden]
+public Vector3 MySpecialVector3;
+```
+
+보다 복잡한 상태를 저장해야 할 경우, JSON 문자열로 직렬화하고 필요할 때 역직렬화하는 방법을 사용할 수 있습니다:
+
+```csharp
+[DataInput]
+[Hidden]
+public string MyJSONState;
+
+// Use Newtonsoft.Json to serialize and deserialize
+MyJSONState = JsonConvert.SerializeObject(mySerializableObject);
+mySerializableObject = JsonConvert.DeserializeObject(MyJSONState);
+```
+
+## 영속 데이터 매니저 사용
+
+직렬화할 수 없는 데이터를 저장하려면 `Context.PersistentDataManager`를 사용해 데이터 폴더에 접근할 수 있는 파일 시스템 API를 이용할 수 있습니다. 예를 들어, 커스텀 디렉토리에서 이미지를 불러오고 저장하려면 다음과 같이 사용할 수 있습니다:
+
+```csharp
+var bytes = await Context.PersistentDataManager.ReadFileBytesAsync("MyPlugin/MyProfileImage.png");
+await Context.PersistentDataManager.WriteFileBytesAsync("MyPlugin/MyProfileImage.png", bytes);
+```
+
+## 플러그인 모드 사용
+
+[플러그인 모드](../plugin-mod)는 모드 폴더에 텍스트 에셋을 저장할 수 있으며, 플러그인은 런타임 시 이를 액세스할 수 있습니다. 예를 들어 `Animations.json`이라는 JSON 파일을 모드 폴더에 저장하고, 이를 플러그인에서 로드할 수 있습니다:
+
+```csharp
+protected override void OnCreate() {
+ base.OnCreate();
+ var json = ModHost.Assets.Load("Assets/MyModFolder/Animations.json"); // Change the path to match your mod folder structure
+}
+```
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/localization.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/localization.md
new file mode 100644
index 0000000..d356876
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/localization.md
@@ -0,0 +1,79 @@
+---
+sidebar_position: 999
+---
+
+# 현지화
+
+Warudo에는 다국어 플러그인을 만들 수 있는 내장된 현지화 시스템이 있어요.
+
+현재 Warudo는 다음 언어들을 공식적으로 지원해요:
+
+| Language | Code |
+|--------------------|---------|
+| English | `en` |
+| Simplified Chinese | `zh_CN` |
+| Japanese | `ja` |
+
+## 문자열 현지화
+
+모든 내장된 로컬라이즈 문자열은 Warudo 데이터 폴더의 `Localizations` 디렉터리에 저장돼 있어요. 해당 JSON 파일을 열면, 문자열 ID와 현지화된 문자열을 키-값 쌍으로 볼 수 있어요.
+
+현지화된 문자열을 가져오려면 문자열 ID에 `Localized()` 확장 메서드를 호출하면 돼요:
+
+```
+using Warudo.Core.Localization; // Import the namespace that contains the extension method
+
+// Assume the user language is set to English
+"FACE_TRACKING".Localized() // "Face Tracking"
+"ALIVE_TIME_DESCRIPTION".Localized() // "The prop will be destroyed after this time."
+```
+
+만약 현재 언어로 아직 현지화되지 않은 문자열이면 해당 문자열은 영어로 대체되고, 영어에서도 찾을 수 없으면 문자열 ID가 반환돼요.
+
+:::tip
+Unity의 `HumanBodyBones` 열거형도 `Localized()` 확장 메서드를 사용해 현지화할 수 있어요. 예를 들어, `HumanBodyBones.Head.Localized()`는 영어로 "Head", 중국어 간체로는 "头"를 반환해요.
+:::
+
+## 현지화 추가하기
+
+[플러그인 모드](../plugin-mod)를 만들고 있다면, 현지화를 추가하는 가장 권장되는 방법은 `Localizations`라는 디렉터리를 모드 폴더에 만드는 것이에요. 이 디렉터리 안에 지원하고 싶은 각 언어에 대한 JSON 파일을 만들어야 해요. JSON 파일은 다음 형식으로 로컬라이즈된 문자열을 포함해야 해요:
+
+```json
+{
+ "en": {
+ "MY_STRING": "My String"
+ },
+ "zh_CN": {
+ "MY_STRING": "我的字符串"
+ },
+ "ja": {
+ "MY_STRING": "私の文字列"
+ }
+}
+```
+
+:::info
+언어마다 하나의 JSON 파일을 만들 수도 있어요.
+:::
+
+플러그인이 로드될 때, Warudo는 자동으로 JSON 파일에서 현지화된 문자열을 불러와요.
+
+만약 플러그인 모드를 만들고 있지 않다면 `Context.LocalizationManager`를 사용해 직접 현지화된 문자열을 추가할 수도 있어요.
+
+:
+
+```csharp
+var localizationManager = Context.LocalizationManager;
+localizationManager.SetLocalizedString("MY_STRING", "en", "My String");
+localizationManager.SetLocalizedString("MY_STRING", "zh_CN", "我的字符串");
+localizationManager.SetLocalizedString("MY_STRING", "ja", "私の文字列");
+```
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/mixins.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/mixins.md
new file mode 100644
index 0000000..494e73c
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/mixins.md
@@ -0,0 +1,110 @@
+---
+sidebar_position: 70
+---
+
+# 믹스인
+
+믹스인은 상속을 사용할 수 없는 상황에서 다양한 엔티티 간에 코드를 재사용하는 방법이에요. 데이터 인풋과 트리거를 포함한 재사용 가능한 로직의 집합이라고 생각할 수 있어요.
+
+## 타입 정의
+
+`Mixin` 타입을 상속하여 믹스인 타입을 정의할 수 있어요. 아래와 같이 정의해요:
+
+```csharp
+public class PositionMixin : Mixin {
+ [DataInput]
+ public Vector3 Position;
+
+ [Trigger]
+ public void GeneratePosition() {
+ SetDataInput(nameof(Position), new Vector3(Random.value, Random.value, Random.value), broadcast: true);
+ }
+}
+```
+
+이제 이 믹스인을 엔티티 서브타입에 포함할 수 있어요. 예를 들어, `PositionMixin`을 커스텀 에셋에 포함하려면 다음과 같이 해요:
+
+```csharp
+[AssetType(...)]
+public class PositionAsset : Asset {
+
+ [Mixin]
+ public PositionMixin PositionMixin;
+
+}
+```
+
+에셋과 마찬가지로 노드에도 믹스인을 포함할 수 있어요:
+
+```csharp
+[NodeType(...)]
+public class PositionNode : Node {
+
+ [Mixin]
+ public PositionMixin PositionMixin;
+
+}
+```
+
+믹스인을 포함할 때는 `[Mixin]` 속성을 추가해야 해요. 또한 엔티티가 생성될 때 자동으로 인스턴스화되므로 값을 직접 할당할 필요는 없어요. 믹스인의 데이터 인풋과 트리거에 바로 접근할 수 있어요:
+
+```csharp
+// Within entity
+public override void OnCreate() {
+ base.OnCreate();
+ var initialPosition = PositionMixin.Position;
+ PositionMixin.GeneratePosition();
+}
+```
+
+하지만 `SetDataInput`과 `InvokeTrigger` 같은 메서드는 믹스인 자체에서는 사용할 수 없어요. 데이터 인풋과 트리거는 기술적으로 믹스인을 포함하는 엔티티에 속해 있으므로 다음과 같이 작성해야 해요:
+
+```csharp
+// Within entity
+SetDataInput(nameof(PositionMixin.Position), new Vector3(1, 2, 3), broadcast: true);
+InvokeTrigger(nameof(PositionMixin.GeneratePosition));
+```
+
+믹스인 내부에서 엔티티와 해당 메서드에 접근하려면 `Owner` 속성을 사용해요:
+
+```csharp
+// Within mixin
+Position = new Vector3(9, 8, 7);
+Owner.BroadcastDataInput(nameof(Position));
+Owner.Watch(nameof(Position), OnPositionChanged);
+```
+
+## 구조화 데이터와의 차이점
+
+믹스인은 [구조화된 데이터](structured-data)와 비슷하게 엔티티에 포함되고 데이터 인풋과 트리거를 가질 수 있지만, 동작 방식은 매우 달라요:
+
+- 구조화된 데이터는 데이터 인풋에 내장되고 직렬화되지만, 믹스인은 데이터 인풋과 트리거를 포함한 엔티티에 "평탄화"돼요.
+- 구조화된 데이터는 주로 데이터를 캡슐화하는 데 집중하므로, 로직을 담고 있지 않으며 믹스인보다 [라이프사이클 이벤트](#behavioral-mixins)가 적어 더 "가벼운" 구조를 가집니다.
+- 엔티티에는 구조화된 데이터 배열을 포함할 수 있지만, 특정 타입의 믹스인은 엔티티에 하나만 포함할 수 있어요.
+
+어느 것을 사용할지 확신이 없다면, 먼저 구조화된 데이터를 사용하는 것을 권장해요!
+
+## 행동 기반 믹스인 {#behavioral-mixins}
+
+믹스인에 엔티티 라이프사이클 이벤트가 필요하다면 `Mixin` 대신 `BehavioralMixin` instead of `Mixin`을 상속하세요. 이를 통해 `OnUpdate()`, `OnPreUpdate()`, `OnPostUpdate()`, `OnLateUpdate()`, `OnFixedUpdate()` 그리고 `OnEndOfFrame()`메서드를 오버라이드할 수 있어요. 엔티티 라이프사이클에 대한 자세한 내용은 [엔티티](entities#lifecycle) 페이지를 참조하세요.
+
+## 내장 믹스인
+
+Warudo는 여러 내장 믹스인을 제공하며, 이를 엔티티에 사용할 수 있어요:
+
+- `Attachable`: 엔티티의 게임오브젝트를 다른 에셋 변환에 연결하는 기능을 추가해요.
+- `PlaybackMixin`: 에디터에 재생 제어 기능을 추가하며, 음악 플레이어, MMD 플레이어, 모션 플레이어에서 사용돼요.
+- `ToolbarItemMixin`: 툴바 아이콘을 추가해요. 이 믹스인은 플러그인에서 툴바에 아이콘을 표시하는 데 사용됩니다. 플러그인에 대한 자세한 내용은 [플러그인](plugins) 페이지를 참조하세요.
+
+:::warning
+현재 내장 믹스인 타입에 대한 문서화는 미흡한 상태이며, 이를 개선하기 위해 노력 중이에요. 그동안은 사용 예제를 참고하기 위해 해당 믹스인을 상속하는 클래스를 참조하는 것을 권장해요.
+:::
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/nodes.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/nodes.md
new file mode 100644
index 0000000..9062b3f
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/nodes.md
@@ -0,0 +1,170 @@
+---
+sidebar_position: 30
+---
+
+# 노드
+
+노드는 서로 연결되어 복잡한 동작을 만들 수 있는 로직 단위예요. 프로그램에서 함수처럼 동작하지만, 시각적으로 표현된다고 생각하면 돼요.
+
+## 타입 정의
+
+노드 타입을 생성하여 blueprint에 인스턴스화하고 저장할 수 있어요. 노드 타입은 `Node` 클래스를 상속하고, `[NodeType]` 속성으로 정의돼요. 아래는 그 예시예요:
+
+```csharp
+[NodeType(
+ Id = "c76b2fef-a7e7-4299-b942-e0b6dec52660",
+ Title = "Hello World",
+ Category = "CATEGORY_DEBUG",
+ Width = 1f
+)]
+public class HelloWorldNode : Node {
+ // Node implementation
+}
+```
+
+파라미터 요약:
+
+- **`Id`**: 노드 타입의 고유 식별자; 각 새로운 노드 타입에 대해 [새로운 GUID를 생성](https://www.guidgenerator.com/online-guid-generator.aspx)해야 해요. 이는 노드 인스턴스의 UUID(`node.Id`)와는 달라요.
+- **`Title`**: 노드 팔레트에 표시될 노드 타입의 이름.
+- **`Category`**: 선택 사항. 노드 팔레트에서 노드를 그룹화하는 데 사용돼요.
+- **`Width`**: 선택 사항. 기본적으로 노드의 너비는 `1f`인데, `2f`로 설정하면 해당 노드는`1f`너비의 노드보다 두 배의 공간을 차지해요.
+
+:::info
+자주 사용되는 노드 카테고리: `CATEGORY_ARITHMETIC`, `CATEGORY_ASSETS`, `CATEGORY_BLENDSHAPES`, `CATEGORY_CHARACTERS`, `CATEGORY_DATA`, `CATEGORY_DEBUG`, `CATEGORY_EVENTS`, `CATEGORY_FLOW`, `CATEGORY_INPUT`, `CATEGORY_MOTION_CAPTURE`, `CATEGORY_INTERACTIONS`.
+
+직접 카테고리 이름을 사용할 수도 있지만, 위 카테고리들은 사용자 언어로 자동으로 현지화돼요.
+:::
+
+## 컴포넌트
+
+노드 타입은 데이터 인풋, 데이터 아웃풋, 플로우 인풋, 플로우 아웃풋, 트리거를 정의할 수 있어요.
+
+![](/doc-img/en-custom-node-1.png)
+
+## 라이프사이클
+
+[엔티티](entities#lifecycle) 페이지에 나와 있는 라이프사이클 단계 외에도, 노드는 다음과 같은 추가적인 라이프사이클 단계가 있어요:
+
+- **`OnAllNodesDeserialized()`:** 해당 blueprint의 모든 노드가 역직렬화된 후에 호출됩니다. 같은 blueprint의 다른 노드에 접근해야 할 때 유용해요.
+- **`OnUserAddToScene()`:** 사용자가 노드를 노드 팔레트에서 드래그하여 에디터에 추가할 때 호출됩니다.
+
+## 트리거링 플로우
+
+플로우 인풋 메서드에서 플로우 아웃풋의 `Continuation`을 반환함으로써 플로우 아웃풋을 트리거할 수 있어요. 예를 들어:
+
+```csharp
+[FlowInput]
+public Continuation Enter() {
+ // Do something
+ return Exit;
+}
+
+[FlowOutput]
+public Continuation Exit;
+```
+
+만약 플로우가 여기서 끝난다면, `null`을 반환하여 플로우가 종료되었음을 나타낼 수 있어요.
+
+```csharp
+[FlowInput]
+public Continuation Enter() {
+ // Do something
+ return null;
+}
+```
+
+때로는 플로우 아웃풋을 지연시키거나 수동으로 트리거하고 싶을 수 있어요. 이럴 때는 `InvokeFlow(string flowOutputPortKey)` 메서드를 사용하여 플로우 아웃풋을 트리거할 수 있어요. 예를 들어:
+
+```csharp
+[FlowInput]
+public Continuation Enter() {
+ async void TriggerExitLater() {
+ await UniTask.Delay(TimeSpan.FromSeconds(5));
+ InvokeFlow(nameof(Exit)); // Start a new flow from the "Exit" output port
+ }
+ return null; // You still need to return a Continuation. Since *this* flow technically ends here, we return null
+}
+```
+
+## 비직렬화 데이터 인풋 & 아웃풋
+
+노드만이 가지고 있는 특별한 점은 비직렬화 데이터 인풋 및 아웃풋을 가질 수 있다는 점이에요. (에셋이나 플러그인에서는 불가능하며, 아예 표시되지 않아요.) 이를 통해 노드 간에 Unity 오브젝트인 `GameObject`나 `Texture2D` 같은 데이터를 주고받고 처리할 수 있어요.
+
+다음과 같이 제네릭 데이터 인풋 또는 아웃풋도 가질 수 있어요:
+
+```csharp
+[DataInput]
+public object MyGenericInput; // Note it's 'object', not 'Object'
+
+[DataOutput
+public object MyGenericOutput() => ...
+```
+
+어떤 타입의 데이터 아웃풋 포트든 `MyGenericInput`에 연결할 수 있어요(값은 단순히 `object`로 업캐스트 돼요). 반대로, `MyGenericOutput`은 어떤 데이터 인풋 포트에도 연결될 수 있지만, 인풋 포트 타입이 `MyGenericOutput`이 반환하는 타입과 호환되지 않으면 인풋 포트로 받은 값은 `null`이 됩니다.
+
+## 타입 컨버터
+
+노드를 연결할 때, Warudo에서 자동으로 데이터 타입을 변환할 수 있어요. 예를 들어 `float`아웃풋을 `int` 인풋에 연결하면 값이 단순히 소수점 이하가 잘려서 변환돼요.
+
+`DataConverters` 클래스를 사용해서 커스텀 타입 컨버터를 등록할 수 있어요. 가장 쉬운 방법은 `DataConverter` 클래스를 구현하는 거예요:
+
+```csharp
+public class MyFloatToIntConverter : DataConverter {
+ public override int Convert(float data) => (int) data;
+}
+```
+
+그런 다음, [플러그인](plugins)의 `OnCreate` 메서드에서 컨버터를 등록할 수 있어요:
+
+```csharp
+public override void OnCreate() {
+ base.OnCreate();
+ DataConverters.RegisterConverter(new MyFloatToIntConverter());
+}
+```
+
+:::caution
+노드 타입의 `OnCreate`에서 타입 컨버터를 등록하지 않아야 해요. 그렇게 하면 노드 인스턴스가 생성될 때마다 컨버터를 등록하게 돼요!
+:::
+
+## 코드 예시
+
+### 기본
+
+- [ExampleNode.cs](https://github.com/HakuyaLabs/WarudoPlaygroundExamples/blob/master/ExampleNode.cs)
+다양한 노드 컴포넌트의 기본 형식을 보여주는 표준 예시예요.
+
+- [StructuredDataExampleNode.cs](https://gist.github.com/TigerHix/81cfa66a8f810165c426d1b5157677b5)
+"내부" 데이터 타입을 생성해요.(구조화된 데이터)
+
+- [GetRandomSoundNode.cs](https://gist.github.com/TigerHix/f0f1a7e3c53ca65450fdca1ff06eb343)
+랜덤 사운드 노드를 가져오는 예시예요.
+
+- [LiteralCharacterAnimationSourceNode.cs](https://gist.github.com/TigerHix/2dc58213defe400ddb280a8cc1e6334b)
+캐릭터 애니메이션 (소스) 노드예요.
+
+- [SmoothTransformNode.cs](https://gist.github.com/TigerHix/eaf8e05e5e1b687b8265420b9943903d)
+부드러운 트랜스폼 노드예요.
+
+### 고급
+
+- [FindAssetByTypeNode.cs](https://gist.github.com/TigerHix/ab3522bb25669457cc583abc4fb025d2)
+AutoCompleteList (드롭다운) 예시예요.
+
+- [MultiGateNode.cs](https://gist.github.com/TigerHix/8747793a68f0aa15a469f9823812e221)
+동적 데이터/플로우 포트 예시예요.
+
+- [ThrowPropAtCharacterNode.cs](https://gist.github.com/TigerHix/18e9f20152c0cfac38fd5528c7af16b6)
+캐릭터에게 소품을 던지는 예시예요.
+
+- [SpawnStickerNode.cs](https://gist.github.com/TigerHix/fe35442e9052cd8c4ea80e0261349321)
+로컬/온라인 이미지 스티커를 생성하는 예시예요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/overview.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/overview.md
new file mode 100644
index 0000000..f74314c
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/overview.md
@@ -0,0 +1,29 @@
+---
+sidebar_position: 0
+---
+
+# 개요
+
+:::tip
+이 섹션은 Warudo의 스크립팅 API에 대해 깊이 다루고 있어요. Warudo에서 스크립팅을 시작하기 위해 모든 세부 사항을 다 알 필요는 없지만, 이 섹션을 대략적으로 훑어보고 어떻게 작동하는지 감을 잡는 것을 추천해요!
+:::
+
+Warudo는 두 개의 애플리케이션을 하나로 결합한 프로그램이에요: 에디터와 런타임 (비프로그래머 섹션에서는 메인 창이라고 불려요). 에디터에서는 씬을 만들고 수정하며, 에셋과 플러그인 설정을 업데이트하고, blueprint에서 노드를 추가하고 연결해요. 런타임은 렌더링/스크립팅 엔진 역할을 하며, 당신이 만든 씬을 실행해요.
+
+![](/doc-img/en-scripting-concepts-1.png)
+
에디터 (오른쪽) 그리고 런타임 (왼쪽).
+
+에디터와 런타임은 웹소켓(WebSocket) 연결을 통해 서로 소통해요. 에디터에서 변경을 하면, 그 변경 사항이 런타임으로 전송되어 씬이 실시간으로 업데이트돼요. 이 아키텍처 덕분에 변경 사항을 즉시 확인할 수 있죠. 반대로, 런타임에서 변경(예: 노드의 데이터 인풋을 프로그래밍 방식으로 변경)을 하면, 그 변경 사항을 에디터로 다시 보내야 해요.([데이터 인풋 액세스](ports-and-triggers#accessing-data-inputs)를 참고하세요.)
+
+Warudo 런타임은 [Unity 2021.3](https://unity.com/)을 기반으로 구축되었지만, 코어 프레임워크는 특정 엔진에 종속되지 않도록 설계되었어요. 이 덕분에 노드, 에셋, 플러그인들이 서로 및 사용자와 간단명료한 API를 통해 상호작용할 수 있으며, [핫 리로딩](../playground)과 같은 기능도 가능해요.
+
+일반적인 Unity 프로젝트에서 `MonoBehaviour`를 상속받아 C# 클래스를 작성하는 대신, Warudo의 기본 클래스인 `노드`와 `에셋`을 상속받는 클래스를 작성하게 돼요. 이 클래스들은 Warudo 런타임에서 인스턴스화되어 관리되고, 그 안에서 Unity API를 호출해 Unity 씬과 상호작용할 수 있어요. 예를 들어, 게임 오브젝트를 생성하거나 컴포넌트를 추가하는 등의 작업이 가능해요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/plugins.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/plugins.md
new file mode 100644
index 0000000..300a2dd
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/plugins.md
@@ -0,0 +1,158 @@
+---
+sidebar_position: 50
+---
+
+# 플러그인
+
+플러그인은 Warudo에서 모든 커스텀 스크립트의 상위 개념이에요. 또한 플러그인은 [리소스 프로바이더와 리졸버](resource-providers-and-resolvers)를 등록하거나, 외부 서비스와 인증을 처리하거나, 플러그인의 에셋과 노드의 전역 설정을 사용자가 구성할 수 있게 하는 등 씬과는 독립적인 작업을 수행하는 데 사용돼요.
+
+:::tip
+플러그인을 생성하지 않고도 [플레이그라운드](../playground)를 사용해 커스텀 에셋과 노드를 불러올 수 있으며, 이들은 Warudo의 "Core" 플러그인에 의해 자동으로 등록되고 관리돼요. 그러나 커스텀 에셋과 노드를 다른 사용자와 공유하려면 [플러그인 모드](../plugin-mod)를 만들어야 하며, 이 안에서 커스텀 에셋과 노드를 등록할 수 있는 자체 플러그인을 만들어야 해요.
+:::
+
+:::info
+이 페이지에서는 플러그인을 만들기 위한 스크립팅 API에 대해 설명하고 있어요. [Plugin Mod](../plugin-mod) 페이지도 참조하세요.
+:::
+
+## 타입 정의
+
+Warudo에서 로드되고 실행될 수 있는 플러그인 타입을 만들 수 있어요. 플러그인 타입은 `Plugin` 타입을 상속받으며, 아래와 같이 `[PluginType]` 속성으로 정의돼요:
+
+```csharp
+[PluginType(
+ Id = "hakuyatira.helloworld",
+ Name = "Hello World",
+ Description = "A simple plugin that says hello to the world.",
+ Version = "1.0.0",
+ Author = "Hakuya Tira",
+ Icon = null,
+ SupportUrl = "https://docs.warudo.app",
+ AssetTypes = new [] { typeof(HelloWorldAsset) },
+ NodeTypes = new [] { typeof(HelloWorldNode) }
+)]
+public class HelloWorldPlugin : Plugin {
+ // Plugin implementation
+}
+```
+
+파라미터 요약:
+
+- **`Id`**: 플러그인 타입의 고유 식별자. 역 도메인 이름 표기법을 사용하는 것이 권장돼요 (예: `com.example.pluginname`). 플러그인 타입은 싱글톤이므로, 플러그인의 `Id` 속성(`plugin.Id`)과 플러그인 타입의 ID(`plugin.PluginType.Id`)는 동일해요.
+- **`Name`**: 플러그인의 이름.
+- **`Description`**: 플러그인에 대한 간단한 설명.
+- **`Version`**: 플러그인의 버전. [Semantic Versioning](https://semver.org/) 표준을 따르는 것이 권장돼요.
+- **`Author`**: 플러그인의 저자.
+- **`Icon`**: 선택사항. 플러그인의 SVG 아이콘. `const string` 필드를 사용해 이 값을 저장하는 것이 권장돼요.
+- **`SupportUrl`**: 선택사항. 플러그인의 지원 페이지 URL.
+- **`AssetTypes`**: 이 플러그인에 포함된 에셋 타입의 배열.
+- **`NodeTypes`**: 이 플러그인에 포함된 노드 타입의 배열.
+
+:::tip
+`icon`은 단일 SVG 요소여야 해요 (예: ``). 예를 들면 다음과 같아요:
+```html
+
+```
+:::
+
+## 라이프사이클
+
+플러그인은 [엔티티](entities#lifecycle) 페이지에서 설명한 라이프사이클 단계 외에도 추가적인 단계가 있어요:
+
+- **`OneTimeSetup()`:** 플러그인의 데이터 파일이 `Plugins/Data`에 존재하는지에 따라, Warudo가 처음 플러그인을 로드할 때 한 번 호출돼요.
+- **`OnSceneLoaded(Scene scene, SerializedScene serializedScene)`:** 씬이 로드된 후, 모든 에셋과 노드가 역직렬화된 후에 호출돼요.
+- **`OnSceneUnloaded(Scene scene)`:** 씬이 언로드될 때 호출돼요.
+
+## 컴포넌트
+
+플러그인 타입은 데이터 인풋과 트리거를 정의할 수 있어요.
+
+![](/doc-img/en-custom-plugin-1.png)
+
+## 로딩 유니티 에셋 {#loading-unity-assets}
+
+[당신의 첫 플러그인 모드를 만들어보세요](../creating-your-first-plugin-mod) 튜토리얼에서 설명한 것처럼, 플러그인의 모드 폴더에 있는 Unity 에셋을 `ModHost`를 사용해 로드할 수 있어요. 예를 들어, 프리팹을 로드하려면:
+
+```csharp
+var prefab = ModHost.LoadAsset("Assets/MyModFolder/MyPrefab.prefab");
+```
+
+텍스트 에셋(예: JSON 파일)을 로드하려면:
+
+```csharp
+var text = ModHost.LoadAsset("Assets/MyModFolder/MyText.json");
+```
+
+:::tip
+Warudo는 [uMod 2.0](https://trivialinteractive.co.uk/products.html)을 모딩 프레임워크로 사용해요. 에셋 로드에 대한 자세한 정보는 [ModHost documentation](https://trivialinteractive.co.uk/products/documentation/umod_20/scriptingreference/html/T_UMod_ModHost.htm)를 참조하세요.
+:::
+
+## 툴바 아이콘
+
+일부 내장 플러그인이 툴바에 아이콘을 표시하는 것을 볼 수 있을 거예요. 게다가, 그 아이콘은 클릭할 수 있으며, 플러그인의 설정을 여는 데 사용할 수 있어요.
+
+![](/doc-img/en-custom-plugin-2.png)
+
+이를 구현하려면, 플러그인 클래스에 `ToolbarItemMixin` [mixin](mixins)를 포함하면 돼요:
+
+```csharp
+[Mixin]
+public ToolbarItemMixin ToolbarItem;
+
+private bool serverConnected; // Assume this is a field that indicates whether the plugin is connected to an external server
+
+public override void OnCreate() {
+ base.OnCreate();
+ ToolbarItem.SetIcon(ToolbarIcon); // ToolbarIcon is a const string that contains a SVG icon
+ ToolbarItem.OnTrigger = () => Context.Service.NavigateToPlugin(Type.Id); // Open the plugin settings when the icon is clicked
+}
+
+public override void OnUpdate() {
+ base.OnUpdate();
+ ToolbarItem.SetEnabled(serverConnected); // Only show the icon if connected to the server
+ ToolbarItem.SetTooltip("Connected! Current time: " + DateTime.Now); // Set a tooltip that is displayed when the user hovers over the icon
+}
+```
+
+## 노드 및 에셋에서 오너 플러그인 액세스
+
+노드 및 에셋에서 `Plugin` 속성을 사용해 소유 플러그인에 접근할 수 있어요. 예를 들어:
+
+```csharp
+public override void OnCreate() {
+ base.OnCreate();
+ var myPlugin = (HelloWorldPlugin) Plugin;
+ // Do something with myPlugin
+}
+```
+
+## 플러그인 vs. 노드
+
+Warudo에서 커스텀 스크립팅은 [blueprint 시스템](../../blueprints/overview.md)을 보완하는 방식으로 사용될 때 가장 효과적이에요. 만약 `Plugin` 클래스에 너무 많은 로직을 처리하고 있다면, 그 대신 커스텀 노드로 작성할 수 있는지 신중하게 고려해 보세요. 예를 들어, LAN에서 커스텀 UDP 패킷을 받을 때 폭죽 효과를 재생하는 아이디어가 있다면 `Plugin` 클래스가 두 가지를 모두 처리하기보다는, 커스텀 UDP 패킷이 수신되면 출력 플로우를 트리거하는 노드 하나와 입력 플로우가 수신되면 폭죽 효과를 재생하는 노드 하나를 구현하는 것이 더 좋고 유지보수하기도 쉬워요. 이는 또한 사용자가 blueprint에서 더 유연하게 커스텀 노드를 사용할 수 있도록 해줘요.
+
+## 코드 예시
+
+### 기본
+
+- [Example plugin](https://gist.github.com/TigerHix/b78aabffc2d03346ff3da526706ce2ca)
+`Plugin` 클래스의 기본 사용법을 포함한 플러그인 템플릿 (단일 파일).
+
+- [WarudoPluginExamples](https://github.com/HakuyaLabs/WarudoPluginExamples)
+여러 파일로 구성된 완성된 플러그인 예시. **Stream Deck Plugin**과 **VMC Plugin**이 포함되어 있어요.
+ - **Stream Deck Plugin**: WebSocket을 통해 외부 애플리케이션 (Warudo의 [Stream Deck plugin](https://apps.elgato.com/plugins/warudo.streamdeck))과 통신하는 플러그인. WebSocket 서비스 기반 클래스인 `WebSocketService`의 사용 예시를 보여줘요.
+ - **VMC Plugin**: [VMC](https://protocol.vmc.info/english)를 지원하는 모션 캡처 방법으로 추가하는 플러그인으로 `FaceTrackingTemplate`와 `PoseTrackingTemplate`을 등록하는 방법을 보여줘요.
+
+### 고급
+
+- [KatanaAnimations.cs](https://gist.github.com/TigerHix/2cb8052b0e8aeeb7f9cb796dc7edc6a3)
+모드 폴더에서 애니메이션 클립을 로드하고 이를 캐릭터 애니메이션으로 등록하는 커스텀 플러그인이에요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/ports-and-triggers.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/ports-and-triggers.md
new file mode 100644
index 0000000..bc8d076
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/ports-and-triggers.md
@@ -0,0 +1,444 @@
+---
+sidebar_position: 2
+---
+
+# 포트 & 트리거
+
+포트와 트리거는 [엔티티](entities)에 속하며, Warudo 스크립팅에서 가장 중요한 요소라고 할 수 있어요. 이들은 엔티티 간에 데이터를 주고받고, 액션을 트리거하며, 에디터에서 사용자와의 상호작용을 제공해요.
+
+![](/doc-img/en-custom-node-1.png)
+
+![](/doc-img/en-scripting-concepts-4.png)
+
+## 데이터 인풋 포트 {#data-input-ports}
+
+데이터 인풋 포트는 사용자가 에디터를 통해 제공하는 데이터를 받거나, 다른 엔티티로부터 데이터를 받을 때 사용돼요. 데이터 인풋은 다양한 타입을 가질 수 있는데, 문자열, 숫자, 불리언 같은 기본적인 타입부터 [구조화된 데이터](structured-data.md)나 배열 같은 복잡한 타입도 지원해요.
+
+데이터 인풋은 `[DataInput]` 속성으로 표시되며, 엔티티 서브클래스의 public 필드로 정의돼요. [시작하기](getting-started.md) 예제에서 우리는 숫자 슬라이더를 정의하는 `DataInput`을 보았죠:
+
+```csharp
+[DataInput]
+[IntegerSlider(1, 100)]
+public int LuckyNumber = 42;
+```
+
+다양한 예시가 있어요:
+
+- **스트링 인풋:**
+ ```csharp
+ [DataInput]
+ public string MyName = "Alice";
+ ```
+- **열거형 인풋:** (에디터에서 드롭다운으로 표시)
+ ```csharp
+ public enum Color {
+ Red,
+ Green,
+ Blue
+ }
+
+ [DataInput]
+ public Color MyColor = Color.Red;
+ ```
+- **배열 인풋:** (에디터에서 수정 가능한 리스트로 표시)
+ ```csharp
+ [DataInput]
+ public float[] MyFavoriteNumbers = new float[] { 3.14f, 2.718f };
+ ```
+
+아래는 에디터에서 어떻게 보이는지의 예시예요 (이 예에서는 노드를 사용하지만, 에셋과 플러그인에서도 동일하게 적용돼요):
+
+
+
+
+
+
+
+코드에서 지정한 기본값으로 데이터 인풋이 초기화되는 방식에 주목해 주세요.이 기본값들은 사용자가 데이터 인풋 레이블 옆에 있는 "Reset" 버튼을 클릭할 때 다시 할당돼요.
+
+데이터 인풋은 일반적으로 직렬화 가능한 타입을 가집니다. 여기서 "직렬화 가능"이란 Warudo를 종료하고 다시 열었을 때 데이터 값이 저장되고 복원될 수 있음을 의미해요. 다음 타입들이 기본적으로 직렬화 가능해요:
+
+- 기본 타입: `int`, `float`, `bool`, `string` 그리고 모든 열거형 타입
+- Unity 타입: `Vector2`, `Vector3`, `Vector4`, `Color`
+- [구조화된 데이터](structured-data.md) 타입
+- [에셋 레퍼런스](#asset-references)
+- 직렬화 가능한 타입의 배열
+
+노드의 경우, 에디터에서 수정할 수는 없지만 노드 자체에서 처리되는 비직렬화 가능한 데이터 인풋도 정의할 수 있어요. 예를 들어, 다음 코드는 제네릭 `오브젝트` 데이터 인풋에서 `ToString()` 메서드를 호출해요:
+
+```csharp
+[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`이 있어요.
+
+### 속성 {#data-input-attributes}
+
+데이터 인풋 포트는 속성을 사용해 에디터에 추가적인 정보를 제공할 수 있어요. 예를 들어, 이전에 봤던 `[IntegerSlider]` 속성은 데이터 인풋이 1에서 100까지의 범위를 가진 슬라이더로 표시되도록 지정해요. 지원되는 속성은 다음과 같아요:
+
+- `[Label(string label)]`: 데이터 인풋의 라벨을 지정해요.
+- `[HideLabel]`: 데이터 인풋의 라벨을 숨기도록 지정해요.
+- `[Description(string description)]`: 데이터 인풋의 설명을 지정해요.
+- `[HiddenIf(string methodName)]`: 지정된 메서드가 `true`를 반환하면 데이터 인풋이 숨겨지도록 지정해요. 메서드는 엔티티 클래스의 `public` 또는 `protected` 메서드여야 하며, `bool`을 반환해야 해요.
+ ```csharp
+ [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는 상수여야 해요.
+ ```csharp
+ [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` 조건을 만족하면 데이터 인풋이 숨겨지도록 지정해요.
+ ```csharp
+ [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`면, 더 큰 글씨 크기와 배경색 없이 텍스트가 표시돼요.
+
+:::info
+`[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`를 반환해야 해요. 메서드는 비동기일 수 있어요. 만약 `forceSelection`이 `true`면 사용자는 드롭다운 리스트에서만 값을 선택할 수 있으며, 선택하지 않으면 `null` 값이 할당돼요.
+ ```csharp
+ using System.Linq;
+
+ [DataInput]
+ [AutoComplete(nameof(AutoCompleteVipName), forceSelection: true)]
+ public string VipName = "Alice";
+
+ protected async UniTask 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 vipNames; // Entity-controlled runtime data
+
+ // Some other code should update the vipNames list
+ ```
+ :::tip
+ 자동완성 리스트는 컴파일 시점에 알 수 없는 옵션 목록을 제공할 때 유용해요. 예를 들어, 디렉터리에서 파일 목록을 제공하거나, 원격 서버에서 이모티콘 목록을 제공할 때 사용할 수 있어요. Warudo의 내부 노드 및 에셋에서도 매우 자주 사용돼요!
+ :::
+- `[MultilineInput]`:데이터 타입이 `string`이어야 해요. 여러 줄로 구성된 텍스트 입력 필드로 데이터 인풋을 표시하도록 지정해요.
+- `[CardSelect]`: 데이터 타입이 열거형이어야 해요. 카드 선택 리스트로 데이터 인풋을 표시하도록 지정해요. (예: "Camera → Control Mode").
+ ```csharp
+ 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)]` 속성을 사용해 열거형 값의 에디터 라벨을 커스터마이징 할 수 있어요. 예를 들어:
+
+```csharp
+public enum Color {
+ [Label("#FF0000")]
+ Red,
+ [Label("#00FF00")]
+ Green,
+ [Label("#0000FF")]
+ Blue
+}
+```
+
+[속성](#data-input-attributes) 섹션에서 설명한 것처럼 `[Description(string description)]` 과 `[Icon(string icon)]` 속성을 사용해 열거형 입력을 카드 목록으로 표시할 수도 있어요.
+
+:::tip
+`icon`은 단일 SVG 요소여야 해요(예: ``). 예를 들어:
+```html
+
+```
+:::
+
+### 에셋 레퍼런스 {#asset-references}
+
+:::warning
+에셋 레퍼런스는 에셋, 노드, 그리고 플러그인이 아닌 구조화된 데이터에서만 사용할 수 있어요.
+:::
+
+데이터 입력을 사용해 현재 씬에서 다른 에셋을 참조할 수 있어요. 예를 들어, 아래 코드는 `CharacterAsset`을 참조하는 데이터 입력을 정의해요:
+
+```csharp
+[DataInput]
+public CharacterAsset MyCharacter;
+```
+
+에디터에서 사용자는 드롭다운 목록에서 캐릭터 에셋을 선택할 수 있어요:
+
+
+
+
+
+
+
+그 후 참조된 에셋의 포트와 트리거에 접근할 수 있어요:
+
+```csharp
+[FlowInput]
+public Continuation Enter() {
+ if (MyCharacter.IsNonNullAndActive()) {
+ MyCharacter.EnterExpression("Joy", transient: true); // Make the character smile!
+ }
+ return Exit;
+}
+```
+
+:::tip
+에셋이 `null`이거나 비활성 상태인지 확인하려면 `asset.IsNullOrInactive()`를 사용하고, 활성 상태인 경우 `asset.IsNonNullAndActive()`를 사용할 수 있어요..
+:::
+
+에셋을 드롭다운 목록에서 필터링하고 싶다면 어떻게 해야 할까요? `[AssetFilter(string methodName)]` 속성을 사용해 씬에 있는 에셋을 필터링할 메서드를 지정할 수 있어요. 메서드는 해당 에셋 타입의 파라미터를 받아 `bool` 값을 반환하는 `public` 또는 `protected` 메서드여야 해요. 예를 들어:
+
+```csharp
+[DataInput]
+[AssetFilter(nameof(FilterCharacterAsset))]
+public CharacterAsset MyCharacter;
+
+protected bool FilterCharacterAsset(CharacterAsset character) {
+ return character.Active; // Only show active characters
+}
+```
+
+### 프로그래밍 방식으로 데이터 인풋 액세스 {#accessing-data-inputs}
+
+엔티티가 있다고 가정해볼게요. 데이터를 읽는 두 가지 방법이 있어요:
+
+1. 데이터 인풋 필드에 직접 접근하기. 예를 들어, `public int MyNumber = 42;`라는 퍼블릭 데이터 인풋 필드를 가진 노드가 있다면 `node.MyNumber`를 사용해 `MyNumber`의 값을 직접 읽을 수 있어요.
+2. `T GetDataInput(string key)` 또는 `object GetDataInput(string key)` 메서드를 사용하기. 이 메서드는 모든 엔티티에서 사용할 수 있으며, 지정된 이름의 데이터 인풋 값을 반환해요. 예를 들어. `MyNumber`라는 데이터 인풋을 가진 노드가 있을 때, `node.GetDataInput("MyNumber")` 또는 스타일적으로 선호되는 `node.GetDataInput(nameof(node.MyNumber))`를 사용해 `MyNumber` 값을 읽을 수 있어요.
+
+:::tip
+포트의 키는 필드 이름과 동일하며, 포트가 동적으로 추가된 경우는 제외됩니다([동적 포트](#dynamic-ports) 참조).
+:::
+
+두 번째 방법은 데이터를 동적으로 접근해야 할 때 유용해요. 예를 들어, 문자열 변수에 따라 데이터를 접근해야 하는 경우에요. (마찬가지로 [동적 포트](#dynamic-ports)참조.) 그 외에는 두 방법에 큰 차이가 없어요.
+
+마찬가지로, 데이터 인풋에 값을 쓰기 위해서는 데이터 인풋 필드에 직접 값을 할당하거나 `void SetDataInput(string key, T value)` 메서드를 사용할 수 있어요. 예를 들어, `MyNumber`라는 데이터 인풋의 값을 설정하려면 `node.MyNumber = 42` 또는 스타일적으로 선호되는 `node.SetDataInput("MyNumber", 42, broadcast: true)` 또는 `node.SetDataInput(nameof(node.MyNumber), 42, broadcast: true)`을 사용할 수 있어요.
+
+:::tip
+`SetDataInput(nameof(MyNumber), 42, broadcast: true)` 대신 다음과 같이 쓸 수 있어요:
+```csharp
+MyNumber = 42;
+BroadcastDataInput(nameof(MyNumber));
+```
+:::
+
+하지만 이 경우에는 두 번째 방법을 사용하는 것이 **깅력하게 권장**되는데, 이유는 두 가지에요:
+
+1. 데이터 인풋의 변경 사항이 [관찰자](operations#watchers)에게 통지된다는 점.
+2. `broadcast` 파라미터를 `true`로 설정하면, 변경 사항이 에디터에 전송돼요. 그렇지 않으면 `BroadcastDataInput(string key)`를 사용해 수동으로 에디터에 변경 사항을 전송해야 해요.
+
+첫 번째 방법은 다음과 같은 경우에만 사용해야 해요:
+
+1. 매우 자주 데이터를 업데이트하지만, 매번 에디터에 변경 사항을 전송할 필요가 없을 때. 즉, 가끔 `BroadcastDataInput(string key)`를 호출해 성능을 절약할 수 있어요.
+2. 데이터 인풋의 관찰자에게 변경 사항을 통지하고 싶지 않을 때. 이는 드문 경우에요.
+
+## 데이터 아웃풋 포트
+
+데이터 아웃풋 포트는 다른 노드에 데이터를 제공하는 데 사용돼요. 데이터 아웃풋은 노드 하위 클래스에서 `[DataOutput]` 속성으로 정의된 공개 메서드로 정의돼요. 이 메서드는 void가 아닌 타입을 반환해야 해요. 아래는 예시예요:
+
+```csharp
+[DataOutput]
+public int RandomNumber() {
+ return Random.Range(1, 100); // Return a random number between 1 and 100
+}
+```
+
+데이터 아웃풋은 일부 [데이터 인풋 속성](#data-input-attributes)을 지원해요: `[Label]`, `[HideLabel]`, `[Description]`, `[HiddenIf]`, `[DisabledIf]`.
+
+## 플로우 인풋 포트 {#flow-inputs}
+
+플로우 인풋 포트는 다른 노드로부터 플로우 신호를 받아 특정 작업을 실행하는 데 사용돼요. 플로우 인풋은 노드 하위 클래스에서 `[FlowInput]` 속성으로 정의된 공개 메서드로 정의되며, 해당 메서드는 `Continuation`이라는 플로우 아웃풋을 반환해야 해요. 다음은 예시예요:
+
+```csharp
+[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;
+```
+
+플로우 인풋은 일부 [데이터 인풋 속성](#data-input-attributes)을 지원해요: `[Label]`, `[HideLabel]`, `[Description]`. 메서드가 `Enter()`로 명명되고 `[Label]` 속성이 없으면, 에디터의 언어로 'Enter'라는 단어로 자동 설정돼요.
+
+## 플로우 아웃풋 포트
+
+플로우 아웃풋 포트는 다른 노드로 플로우 신호를 보내는 데 사용돼요. 플로우 아웃풋은 노드 하위 클래스에서 `[FlowOutput]` 속성으로 정의된 공개 필드로 정의되며, 필드는 `Continuation` 타입이어야 해요. [플로우 인풋](#flow-inputs)의 예시에서 볼 수 있어요.
+
+플로우 아웃풋은 일부 [데이터 인풋 속성](#data-input-attributes)을 지원해요: `[Label]`, `[HideLabel]`,`[Description]`. 필드 이름이 `Exit`이고 `[Label]` 속성이 없으면, 에디터의 언어로 'Exit'이라는 단어로 자동 설정돼요.
+
+## 트리거
+
+트리거는 에디터에서 클릭해 특정 작업을 실행할 수 있는 버튼이에요. 트리거는 엔티티 하위 클래스에서 `[Trigger]` 속성으로 정의된 공개 메서드로 정의돼요. 아래는 예시예요:
+
+```csharp
+[Trigger]
+public void ShowPopupMessage() {
+ Context.Service.PromptMessage("Title of the message", "Content of the message");
+}
+```
+
+에디터에서 다음과 같이 렌더링돼요:
+
+![](/doc-img/en-scripting-concepts-5.png)
+
+사용자가 버튼을 클릭하면 `ShowPopupMessage` 메서드가 호출돼요.
+
+트리거는 일부 [데이터 인풋 속성](#data-input-attributes)을 지원해요: `[Label]`, `[HideLabel]`, `[Description]`, `[HiddenIf]`, `[DisabledIf]`, `[Section]`, `[SectionHiddenIf]`.
+
+### 비동기 트리거
+
+트리거 메서드는 비동기로 처리될 수 있어요. 예를 들어, 일정 시간이 지난 후 메시지를 표시하려면 `UniTask`를 사용할 수 있어요:
+
+```csharp
+[Trigger]
+public async void ShowPopupMessageAfterDelay() { // Note the async keyword
+ await UniTask.Delay(TimeSpan.FromSeconds(1)); // Wait for 1 second
+ Context.Service.PromptMessage("Title of the message", "Content of the message");
+}
+```
+
+더 실용적인 예로, 진행하기 전에 확인 대화 상자를 표시할 수도 있어요:
+
+```csharp
+[Trigger]
+public async void ShowConfirmationDialog() {
+ bool confirmed = await Context.Service.PromptConfirmation("Are you sure?", "Do you want to proceed?");
+ if (confirmed) {
+ // Proceed
+ }
+}
+```
+
+### 트리거를 프로그래밍 방식으로 호출하기
+
+[데이터 인풋 액세스](#accessing-data-inputs)와 유사하게, 메서드를 직접 호출하거나 엔티티의 `void InvokeTrigger(string key)` 메서드를 사용하여 프로그래밍 방식으로 트리거를 호출할 수 있어요. 예를 들어, `ShowPopupMessage`라는 트리거를 호출하려면 `entity.ShowPopupMessage()` 또는 `entity.InvokeTrigger("ShowPopupMessage")`를 사용할 수 있어요..
+
+## 포트 순서
+
+기본적으로, 포트는 엔티티 클래스에서 선언된 순서에 따라 자동으로 정렬돼요. 하지만 `[DataInput]`, `[DataOutput]`, `[FlowInput]`, `[FlowOutput]`, 그리고 `[Trigger]` 속성의 `order` 파라미터를 사용해 포트 순서를 수동으로 지정할 수 있어요. 예를 들어:
+
+```csharp
+[DataInput(order = 1)]
+public int MyNumber = 42;
+
+[DataInput(order = -1)]
+public string MyString = "Hello, World!"; // This will be displayed before MyNumber
+```
+
+## 동적 포트
+
+특정 조건에 따라 포트를 동적으로 추가하거나 제거하고 싶을 때가 있어요. 예를 들어, 내장된 **Multi Gate** 노드는 "Exit Count" 데이터 인풋에 따라 동적 포트 개수를 가지고 있어요.
+
+이를 위해 런타임에 포트 컬렉션에 접근할 수 있어요:
+
+```csharp
+FlowOutputPortCollection.GetPorts().Clear(); // Clear all flow output ports
+for (var i = 1; i <= ExitCount; i++) {
+ AddFlowOutputPort("Exit" + i, new FlowOutputProperties {
+ label = "EXIT".Localized() + " " + i
+ }); // Create a new flow output port for each exit
+}
+Broadcast(); // Notify the editor that the ports have changed
+```
+
+:::tip
+**Multi Gate** 노드의 전체 소스 코드는 [여기](https://gist.github.com/TigerHix/8747793a68f0aa15a469f9823812e221)에서 찾을 수 있어요.
+:::
+
+:::info
+포트를 너무 자주 추가하거나 제거하지 말아야 해요(예: 매 프레임마다). 성능 문제를 일으킬 수 있기 때문이에요.
+:::
+
+## 동적 포트 속성
+
+포트의 레이블, 설명 또는 타입별 속성과 같은 속성을 동적으로 변경할 수도 있어요. 예를 들어:
+
+```csharp
+[DataInput]
+[IntegerSlider(1, 10)]
+public int CurrentItem = 1;
+
+private int itemCount = 10; // Assume we have 10 items initially
+```
+
+`[IntegerSlider]`의 범위는 컴파일 시에 결정돼요. 하지만 `itemCount`가 변경되면 슬라이더의 범위도 업데이트해야 할 거예요. 이를 위해 포트 속성에 직접 접근할 수 있어요:
+
+```csharp
+var properties = GetDataInputPort(nameof(CurrentItem)).Properties;
+properties.description = $"Select from {itemCount} items."; // Change the description
+
+var typeProperties = (IntegerDataInputTypeProperties) properties.typeProperties; // Get type-specific properties
+typeProperties.min = 1;
+typeProperties.max = itemCount; // Change the slider range
+
+BroadcastDataInputProperties(nameof(CurrentItem)); // Notify the editor that the properties have changed
+```
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/resource-providers-and-resolvers.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/resource-providers-and-resolvers.md
new file mode 100644
index 0000000..76d1999
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/resource-providers-and-resolvers.md
@@ -0,0 +1,185 @@
+---
+sidebar_position: 200
+---
+
+# 리소스 프로바이더와 리졸버
+
+Warudo에서 리소스는 에셋과 노드에서 사용할 수 있는 외부 데이터를 의미해요. 예를 들어, 캐릭터 리소스는 `.vrm` 및 `.warudo` 파일이며, 이는 `Characters` 디렉토리에 저장돼요. 캐릭터 애니메이션 리소스는 Warudo에서 제공하는 500개 이상의 기본 애니메이션과 `CharacterAnimations` 디렉토리에 있는 커스텀 [캐릭터 애니메이션 모드](../../modding/character-animation-mod)를 포함해요. [스크린](../../assets/screen) 이미지 리소스는 `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에서 설치된 캐릭터 모드를 찾아요.
+
+
+
+![](/doc-img/en-resource-providers-1.png)
+
+
+
+리소스 URI를 선택하면 Warudo는 해당 **리소스 URI 리졸버**를 호출해 리소스 데이터를 로드해요. 예를 들어, `.vrm` 파일과 `.warudo` 파일은 각각 다른 리졸버가 로드하지만, 두 리졸버는 모두 `GameObject`를 반환해요(Unity 씬에 로드된 캐릭터). 따라서 캐릭터 에셋은 이 둘을 동일하게 처리할 수 있어요.
+
+리소스 프로바이더와 리졸버는 반드시 [플러그인](plugins)에 의해 등록돼야 해요.
+
+## 프로바이더
+
+큐브와 구체 같은 소품 리소스를 제공하는 커스텀 리소스 프로바이더를 등록하는 토이 플러그인 예제를 살펴볼게요.
+
+```csharp
+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 ProvideResources(string query) {
+ if (query != "Prop") return null; // If the query is not "Prop", we don't have any compatible resources
+ return new List {
+ 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"`이라는 쿼리로 소품 리소스를 요청하기 때문이에요. 예시는 다음과 같아요:
+
+```csharp
+// In the prop asset class
+[AutoCompleteResource("Prop")]
+public string Source;
+```
+
+:::tip
+내장 에셋에서 사용하는 쿼리 목록은 [내장 리소스 타입](#built-in-resource-types) 섹션에서 확인할 수 있어요.
+:::
+
+:::tip
+`prop://primitives/cube`와 같은 방식으로 URI를 작성하는데, 소품 리소스에는 `prop://` 스킴을 사용하는 것이 관례지만, 반드시 이를 따를 필요는 없어요. 특히, 리소스 URI에 대한 커스텀 리졸버를 작성하는 경우에는 더 자유롭게 작성할 수 있어요.
+:::
+
+플러그인이 로드된 후, 소품 에셋의 `Source` 드롭다운을 열면, 우리가 커스텀 리소스 프로바이더로 제공한 두 가지 리소스를 확인할 수 있을 거예요.
+
+![](/doc-img/en-resource-providers-2.png)
+
+이 리소스를 선택하면 해당하는 리소스 URI 리졸버가 호출되어 소품 데이터를 로드하게 돼요. 하지만 아무도 우리의 URI를 어떻게 해결할지 모르니, 이제 URI를 해결할 리졸버를 만들어볼게요.
+
+## URI 리졸버
+
+다음 클래스를 플러그인에 추가해 주세요:
+
+```csharp
+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`이므로 게임오브젝트를 직접 반환해요.
+
+:::tip
+반환되는 오브젝트 타입은 해당 리소스를 사용하는 에셋과 호환되어야 해요. 예를 들어, 캐릭터 에셋은 캐릭터 리소스를 선택할 때 `GameObject`를 기대하고, 화면 에셋은 이미지 리소스를 선택할 때 `ImageResource`를 기대해요. 내장 에셋에서 기대하는 타입 목록은 [내장 리소스 타입](#built-in-resource-types) 섹션에서 확인할 수 있어요.
+:::
+
+이제 플러그인의 `OnCreate` 메서드에서 리졸버를 등록하기만 하면 돼요:
+
+```csharp
+Context.ResourceManager.RegisterUriResolver(new PrimitivePropResourceUriResolver(), this);
+```
+
+플러그인이 다시 로드되면, 소품 에셋에서 큐브나 구체를 선택할 때 Warudo가 Unity 씬에 큐브나 구체를 생성할 거예요!
+
+## 모드 컬렉션
+
+리소스 프로바이더와 리졸버의 일반적인 사용 사례 중 하나는 모드 컬렉션을 제공하는 거예요. 예를 들어, Unity에 100개의 소품 프리팹이 있고 이를 Warudo에서 사용하고 싶다면, 100개의 [소품 모드](../../modding/prop-mod)를 `Props` 디렉토리에 익스포트하는 대신, 플러그인의 모드 폴더에서 프리팹을 직접 로드하는 커스텀 리소스 프로바이더와 리졸버를 작성할 수 있어요(자세한 내용은 [로딩 Unity 에셋](plugins#loading-unity-assets) 참고). 이렇게 하면 모든 리소스를 드롭다운에서 동일한 카테고리로 볼 수 있는 추가 이점도 얻을 수 있어요.
+
+:::tip
+모드 컬렉션 플러그인을 작성하는 모범 사례는 [카타나 애니메이션](https://gist.github.com/TigerHix/2cb8052b0e8aeeb7f9cb796dc7edc6a3) 샘플 플러그인을 참고하세요.
+:::
+
+## 커스텀 리소스 타입
+
+리소스는 제네릭하게 설계되었기 때문에 어떤 종류의 데이터에도 사용할 수 있어요. 예를 들어, 사용자가 이모트를 스폰할 수 있게 해주는 플러그인을 작성하려 한다면 `Emote`라는 커스텀 리소스 타입을 만들 수 있어요::
+
+```csharp
+[AutoCompleteResource("Emote")]
+public string Emote; // This will be used by the user to select the emote URI
+```
+
+그런 다음, 쿼리가 `"Emote"`일 때 이모트 URI(예: `emote://xxx/yyy`) 목록을 제공하는 커스텀 리소스 프로바이더와, URI 스킴이 커스텀 스킴과 일치할 때 이모트 데이터를 로드하는 커스텀 리소스 URI 리졸버를 작성할 수 있어요.
+
+## 내장 리소스 타입 {#built-in-resource-types}
+
+내장 에셋에서 사용하는 내장 리소스 타입 목록이에요:
+
+| Query | Example(s) | Expects |
+|--------------------|------------------------------------------|--------------------------------------------|
+| `"Character"` | Character | `GameObject ` |
+| `"CharacterAnimation"` | Character, Play Character Idle Animation | `AnimationClip` |
+| `"Environment"` | Environment | `Scene` or `ValueTuple` |
+| `"Image"` | Screen | `Warudo.Plugins.Core.Utils.ImageResource` |
+| `"Music"` | Music Player | `string` (absolute file path) |
+| `"Particle"` | Throw Prop At Character | `GameObject` |
+| `"Prop"` | Prop | `GameObject` |
+| `"Sound"` | Play Sound, Throw Prop At Character | `AudioClip` |
+| `"Video"` | Screen | `string` (absolute file path) |
+
+## 썸네일 리졸버
+
+리소스 드롭다운이 `[PreviewGallery]` 속성으로 주석 처리된 경우, 사용자는 "Preview Gallery" 버튼을 클릭하여 리소스의 썸네일 그리드를 볼 수 있어요. 이 기능은 리소스가 이미지, 소품, 포즈 또는 기타 시각적 데이터를 포함할 때 유용해요.
+
+썸네일을 제공하려면 `IResourceUriThumbnailResolver` 인터페이스를 구현하여 비동기적으로 썸네일 이미지 데이터의 `byte[]`를 반환해야 해요. 그런 다음, 플러그인의 `OnCreate` 메서드에서 리졸버를 등록하세요:
+
+```csharp
+Context.ResourceManager.RegisterUriThumbnailResolver(new MyUriThumbnailResolver(), this);
+```
+
+:::tip
+최상의 경험을 위해 썸네일 이미지는 50ms 이내에 로드될 수 있도록 하세요.
+:::
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/scene.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/scene.md
new file mode 100644
index 0000000..151591e
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/scene.md
@@ -0,0 +1,45 @@
+---
+sidebar_position: 1
+---
+
+# 씬
+
+씬은 에셋 목록, blueprint 목록, 그리고 해당 씬에 대한 플러그인 설정을 저장하는 JSON 파일이에요. 저장된 씬을 에디터에서 열면, 저장된 에셋이 먼저 생성되고 역직렬화(즉, 복원)됩니다. 그 후에, 저장된 blueprint가 하나씩 인스턴스화되며, 각 노드는 생성되고 역직렬화돼요.
+
+:::info
+코드베이스에서는 blueprint를 _graphs_라고 부릅니다.
+:::
+
+## 씬 데이터 액세스
+
+현재 열린 씬의 데이터를 `Context.OpenedScene`을 사용하여 접근할 수 있어요. 그런 다음, 씬 내의 에셋, blueprint 등을 접근할 수 있습니다:
+
+```csharp
+var scene = Context.OpenedScene;
+
+var assets = scene.GetAssets();
+var characterAssets = scene.GetAssets();
+var blueprints = scene.GetGraphs();
+```
+
+새로운 에셋이나 노드를 인스턴스화할 수 있어요:
+
+```csharp
+var newCharacterAsset = scene.AddAsset(); // Instantiate a new character asset
+var newCharacterAssetByTypeId = scene.AddAsset("726ab674-a550-474e-8b92-66526a5ad55e"); // Instantiate a new character asset by type ID
+
+var blueprint = scene.GetGraphs().Values.First(); // Get the first blueprint in the scene
+var newNode = blueprint.AddNode(); // Instantiate a new node
+var newNodeByTypeId = blueprint.AddNode("e931f780-e41e-40ce-96d0-a4d47ca64853"); // Instantiate a new node by type ID
+
+Context.Service.BroadcastOpenedScene(); // Send the updated scene to the editor
+```
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/service.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/service.md
new file mode 100644
index 0000000..cee1545
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/service.md
@@ -0,0 +1,37 @@
+---
+sidebar_position: 75
+---
+
+# 에디터와의 통신 {#service}
+
+런타임과 에디터 간의 모든 통신은 `Service` 클래스를 통해 이루어지며, 이는 `Context` 싱글턴 오브젝트를 통해 접근할 수 있어요. 예를 들어, 에디터에서 팝업 메시지를 표시하려면 다음 코드를 사용할 수 있습니다:
+
+```csharp
+Context.Service.PromptMessage("Hey!", "I'm a message!");
+```
+
+자주 사용되는 메서드:
+
+- **`void BroadcastOpenedScene()`:** 씬 전체를 에디터로 전송해요. 씬에 에셋이나 blueprint를 프로그램적으로 추가/삭제한 후에만 호출하면 돼요.
+- **`void Toast(ToastSeverity severity, string header, string summary, string message = null, TimeSpan duration = default)`:** 에디터에 토스트 메시지를 표시해요. `message`가 null이 아니면, 사용자는 토스트를 클릭해 전체 메시지를 볼 수 있어요.
+- **`void PromptMessage(string header, string message, bool markdown = false)`:** 에디터에 팝업 메시지를 표시해요. `markdown`이 `true`면, 메시지는 Markdown 형식으로 렌더링돼요.
+- **`UniTask PromptConfirmation(string header, string message)`:** 에디터에 확인 다이얼로그를 표시해요. 사용자가 "OK"를 클릭하면 `true`를 반환하고, "Cancel"을 클릭하면 `false`를 반환해요.
+- **`UniTask PromptStructuredDataInput(string header, T structuredData = null)`:** 에디터에 [구조화된 데이터 인풋 다이얼로그](structured-data#input)를 표시해요. `structuredData` 오브젝트를 전달하면, 해당 데이터로 미리 채워진 다이얼로그가 표시돼요. 사용자가 "OK"를 클릭하면 구조화된 데이터 오브젝트를 반환하고 "Cancel"을 클릭하면 `null`을 반환해요.
+- **`UniTask PromptStructuredDataInput(string header, Action structuredDataInitializer)`:** 위와 비슷하지만, `structuredDataInitializer` 함수가 구조화된 데이터 오브젝트를 초기화하는 데 사용돼요.
+- **`void ShowProgress(string message, float progress, TimeSpan timeout = default)`:** 에디터에 진행률 표시줄을 표시해요. `progress` 값은 0과 1 사이여야 해요. `timeout`이 지정되면, 해당 시간 후에 진행률 표시줄이 자동으로 사라져요.
+- **`void HideProgress()`:** 진행률 표시줄을 숨겨요.
+- **`void NavigateToGraph(Guid graphId, Guid nodeId = default)`:** 에디터에서 지정된 그래프로 이동해요. `nodeId`가 지정되면, 에디터는 해당 그래프에서 지정된 노드를 선택해요.
+- **`void NavigateToPlugin(string pluginId, string port = default)`:** 에디터에서 지정된 플러그인으로 이동해요. `port`가 지정되면, 에디터는 해당 플러그인의 지정된 포트로 이동해요.
+
+:::tip
+`Context.Service`는 에디터가 닫혀 있어도 런타임에서 언제든지 사용할 수 있다고 생각하시면 돼요.
+:::
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/structured-data.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/structured-data.md
new file mode 100644
index 0000000..5c3d898
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/structured-data.md
@@ -0,0 +1,317 @@
+---
+sidebar_position: 60
+---
+
+# 구조화된 데이터
+
+구조화된 데이터는 엔티티 내에서 임베디드 데이터 구조를 정의하는 방식이에요. 이는 복잡한 데이터 인풋을 정의하고, 동일 엔티티 내 또는 여러 엔티티 간에 재사용해야 할 때 유용해요.
+
+## 타입 정의
+
+`StructuredData` 타입을 상속받아 구조화된 데이터 타입을 생성할 수 있어요. 예시는 아래와 같아요:
+
+```csharp
+public class MyTransformData : StructuredData {
+ [DataInput]
+ public Vector3 Position;
+
+ [DataInput]
+ public Vector3 Rotation;
+
+ [DataInput]
+ public Vector3 Scale = Vector3.one;
+
+ [Trigger]
+ public void ResetAll() {
+ Position = Vector3.zero;
+ Rotation = Vector3.zero;
+ Scale = Vector3.one;
+ Broadcast();
+ }
+}
+```
+
+이제 이 구조화된 데이터 타입을 엔티티의 데이터 입력 필드 타입으로 사용할 수 있어요:
+
+```csharp
+[DataInput]
+public MyTransformData MyTransform;
+```
+
+에디터에서는 이렇게 보여요:
+
+
+
+![](/doc-img/en-structured-data-1.png)
+
+
+
+구조화된 데이터 필드에 값을 할당할 필요는 없어요. 엔티티가 생성될 때 자동으로 인스턴스화되므로, 구조화된 데이터의 데이터 인풋에 직접 접근할 수 있어요:
+
+```csharp
+public override void OnCreate() {
+ base.OnCreate();
+ MyTransform.ResetAll();
+}
+```
+
+## 컴포넌트
+
+구조화 데이터 타입은 데이터 인풋과 트리거를 정의할 수 있어요.
+
+
+
+![](/doc-img/en-structured-data-5.png)
+
+
+
+## 라이프사이클
+
+구조화된 데이터는 [Entities](entities#lifecycle)페이지에 나열된 표준 라이프사이클 이벤트 외에 `OnUpdate()`라는 추가적인 라이프사이클 이벤트만 가지고 있어요(예 `OnCreate()`).
+
+:::tip
+구조화 데이터는 데이터 컨테이너로 생각하는 것이 좋아요. 복잡한 로직을 수행하지 말고, 구조화 데이터는 최대한 단순하게 유지하고, 상위 엔티티가 복잡한 작업을 처리하도록 하는 것이 좋습니다.
+:::
+
+## 구조화 데이터 업데이트하기
+
+구조화 데이터는 단지 엔티티일 뿐이므로, 다른 엔티티처럼 데이터 인풋을 업데이트할 수 있어요:
+
+```csharp
+MyTransform.SetDataInput(nameof(MyTransform.Position), new Vector3(1, 2, 3), broadcast: true);
+
+// or
+
+MyTransform.Position = new Vector3(1, 2, 3);
+MyTransform.BroadcastDataInput(nameof(MyTransform.Position));
+```
+
+여러 데이터 인풋을 업데이트할 때는 필드에 직접 할당한 후, `Broadcast` 메서드를 사용해 모든 데이터 인풋을 한 번에 브로드캐스트할 수도 있어요:
+
+```csharp
+MyTransform.Position = new Vector3(1, 2, 3);
+MyTransform.Rotation = new Vector3(0, 0, 0);
+MyTransform.Scale = new Vector3(1, 1, 1);
+MyTransform.Broadcast(); // Or BroadcastDataInput(nameof(MyTransform));
+```
+
+## 중첩된 구조화 데이터
+
+구조화 데이터는 중첩될 수 있어요:
+
+```csharp
+public class MyData1 : StructuredData {
+ [DataInput]
+ public MyData2 NestedData;
+
+ public class MyData2 : StructuredData {
+ [DataInput]
+ public MyData3 NestedData;
+
+ public class MyData3 : StructuredData {
+ [DataInput]
+ public bool SuperNestedBool;
+ }
+ }
+}
+```
+
+## 배열
+
+구조화 데이터 배열도 정의할 수 있어요:
+
+```csharp
+[DataInput]
+public MyTransformData[] MyTransforms;
+```
+
+배열을 초기화할 필요는 없어요. Warudo가 자동으로 빈 배열을 초기화해줘요:
+
+
+
+![](/doc-img/en-structured-data-2.png)
+
+
+
+사용자가 **+** 버튼을 클릭하면, 구조화 데이터 요소가 배열에 추가돼요:
+
+
+
+![](/doc-img/en-structured-data-3.png)
+
+
+
+## 커스텀 이니셜라이저
+
+구조화 데이터 배열의 요소는 Warudo에 의해 자동으로 초기화돼요:
+
+```csharp
+[DataInput]
+public MyTransformData[] MyTransforms; // Each MyTransforms[i].Scale is initialized to (1, 1, 1)
+```
+
+하지만 동적인 값으로 새 요소를 초기화하고 싶을 때가 있을 수 있어요. 이럴 때는 `[StructuredDataInitializer]` 속성을 사용해 새 구조화 데이터 요소를 초기화할 메서드를 지정할 수 있어요:
+
+```csharp
+[DataInput]
+[StructuredDataInitializer(nameof(InitializeTransform))]
+public MyTransformData[] MyTransforms;
+
+protected void InitializeTransform(MyTransformData transform) {
+ transform.Position = new Vector3(Random.value, Random.value, Random.value);
+ transform.Rotation = new Vector3(Random.value, Random.value, Random.value);
+ transform.Scale = new Vector3(Random.value, Random.value, Random.value);
+ // Note there is no need to broadcast - the structured data has not be sent to the editor anyway
+}
+```
+
+사용자가 **+** 버튼을 클릭하면 `InitializeTransform` 메서드가 호출되어 새로운 구조화 데이터 요소가 초기화돼요.
+
+## 프로그래밍 방식으로 구조화 데이터 생성
+
+구조화 데이터 배열을 수동으로 채우거나, 프로그래밍 방식으로 구조화 데이터 요소를 추가하려면 `StructuredData.Create()` 메서드를 사용하세요:
+
+```csharp
+public override void OnCreate() {
+ base.OnCreate();
+
+ // Create a new structured data instance
+ var mySampleData = StructuredData.Create();
+ mySampleData.Position = new Vector3(1, 2, 3);
+ // Or equivalently
+ // var mySampleData = StructuredData.Create(sd => sd.Position = new Vector3(1, 2, 3));
+
+ SetDataInput(nameof(MyTransforms), new [] { mySampleData }, broadcast: true);
+}
+
+[Trigger]
+public void AddNewRandomTransform() {
+ var newTransform = StructuredData.Create();
+ newTransform.Position = new Vector3(Random.value, Random.value, Random.value);
+ newTransform.Rotation = new Vector3(Random.value, Random.value, Random.value);
+ newTransform.Scale = new Vector3(Random.value, Random.value, Random.value);
+
+ var newTransforms = new List(MyTransforms);
+ newTransforms.Add(newTransform);
+
+ SetDataInput(nameof(MyTransforms), newTransforms.ToArray(), broadcast: true);
+}
+```
+
+:::caution
+구조화 데이터 인스턴스를 생성할 때 `new MyTransformData()`를 사용하지 마세요. 항상 `StructuredData.Create()`를 사용해 올바른 엔티티 초기화를 보장하세요.
+:::
+
+## 접을 수 있는 구조화 데이터
+
+공간 절약을 위해 구조화 데이터를 접을 수 있어요:
+
+
+
+![](/doc-img/en-structured-data-4.png)
+
+
+
+`ICollapsibleStructuredData` 인터페이스만 구현하면 돼요:
+
+```csharp
+public class MyTransformData : StructuredData, ICollapsibleStructuredData {
+ // ...
+
+ public string GetHeader() => Position + " " + Rotation + " " + Scale;
+}
+```
+
+`CollapsedSelf` 및 `CollapsedInHierarchy` 속성을 사용해 구조화 데이터가 접혔는지 여부를 확인할 수 있어요. 예를 들어, Unity 씬에서 구조화 데이터의 접힘 상태에 따라 게임오브젝트의 가시성을 토글할 수 있어요:
+
+```csharp
+public override void OnUpdate() {
+ base.OnUpdate();
+ gameObject.SetActive(!CollapsedInHierarchy);
+}
+```
+
+## 부모 엔티티 액세스
+
+구조화 데이터에서 부모 엔티티에 접근해야 할 때는 기본 클래스를 `StructuredData`로 변경하고 `Parent` 속성을 사용해 자동으로 `TParent` 타입으로 캐스팅된 부모 엔티티에 접근할 수 있어요. `MyTransformData` 예제를 보면:
+
+```csharp
+[AssetType(...)]
+public class MyTransformAsset : Asset {
+
+ [DataInput]
+ public MyTransformData MyTransform;
+
+ protected void ParentMethod() {
+ // ...
+ }
+
+ public class MyTransformData : StructuredData {
+ // ...
+
+ public void AccessParentMethod() {
+ Parent.ParentMethod();
+ }
+ }
+}
+```
+
+참고로 `OnCreate`가 호출될 때는 `Parent`가 `null` 이므로, 이 시점에 부모 엔티티에 접근하면 안돼요. 대신, `OnAssignedParent()` 콜백을 구현해야 해요:
+
+```csharp
+public class MyTransformData : StructuredData {
+ public override void OnAssignedParent() {
+ Parent.ParentMethod();
+ }
+}
+```
+
+## 구조화 데이터 인풋 다이얼로그 {#input}
+
+구조화 데이터를 사용하는 가장 흔한 사례 중 하나는 사용자에게 다이얼로그에서 데이터를 입력할 수 있게 하는 거예요. 온보딩 도우미를 기억하나요? 온보딩 과정 중 나타났던 모든 팝업 창이 실제로는 구조화 데이터였어요!
+
+구조화 데이터 인풋 다이얼로그를 만들려면, 구조화 데이터 타입을 정의하고 `Context.Service.PromptStructuredDataInput`를 호출하면 돼요:
+
+```csharp
+[Trigger]
+public async void PromptUserInput() {
+ var sd = await Context.Service.PromptStructuredDataInput("Customize Your Transform");
+ if (sd == null) return; // The user clicked cancel
+
+ Context.Service.Toast(ToastSeverity.Success,
+ "Thank you for your input!",
+ "Your new transform is: " + sd.GetHeader());
+ SetDataInput(nameof(MyTransform), sd, broadcast: true);
+}
+```
+
+`[Markdown(primary: true)]` 속성을 문자열 데이터 인풋에 적용하고 `[CardSelect]` 속성을 열거형 데이터 인풋에 적용하며, `[HiddenIf]`를 적절히 사용하면 플러그인에서 깔끔하고 사용자 친화적인 다이얼로그를 만들 수 있어요!
+
+### 재시도 다이얼로그
+
+현재 다이얼로그를 다시 표시하려면, 현재 구조화 데이터를 사용해 `PromptStructuredDataInput`을 다시 호출하면 돼요:
+
+```csharp
+[Trigger]
+public async void PromptUserInput() {
+ var sd = await Context.Service.PromptStructuredDataInput("Customize Your Transform");
+ if (sd == null) return; // The user clicked cancel
+
+ while (sd.Position == Vector3.zero) {
+ Context.Service.Toast(ToastSeverity.Error, "Invalid Input", "Position cannot be zero!");
+ sd = await Context.Service.PromptStructuredDataInput("Customize Your Transform", sd); // Note the second parameter
+ if (sd == null) return; // The user clicked cancel
+ }
+}
+```
+
+이 방법은 사용자가 구조화 데이터에 입력한 내용을 유지하면서 잘못된 입력을 수정할 수 있도록 해줘요.
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/watchers.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/watchers.md
new file mode 100644
index 0000000..80fb413
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/api/watchers.md
@@ -0,0 +1,107 @@
+---
+sidebar_position: 80
+---
+
+# 데이터 인풋 감시자 {#watchers}
+
+종종 데이터 인풋의 값이 변경될 때 이를 알아야 할 경우가 있어요. 예를 들어, 다음 코드에서 사용자가 데이터 폴더의 `MyPluginFiles` 디렉토리에서 파일을 선택하도록 하려면:
+
+```csharp
+[DataInput]
+[AutoCompleteList(nameof(AutoCompletePluginFiles), forceSelection: true)]
+public string SelectedFile;
+
+protected async UniTask AutoCompletePluginFiles() {
+ return AutoCompleteList.Single(Context.PersistentDataManager.GetFileEntries("MyPluginFiles").Select(it => new AutoCompleteEntry {
+ label = it.fileName,
+ value = it.path
+ }));
+}
+```
+
+사용자가 새로운 파일을 선택했을 때 이를 알 수 있으면 좋겠죠. 가장 단순한 방법은 `SelectedFile`의 값을 매 프레임마다 확인하는 것이지만, 이는 비효율적이고 번거로워요. 대신 `OnCreate()`에서 감시자를 등록할 수 있어요::
+
+```csharp
+protected override void OnCreate() {
+ base.OnCreate();
+ Watch(nameof(SelectedFile), OnSelectedFileChanged);
+}
+
+protected void OnSelectedFileChanged(string from, string to) {
+ if (to == null) {
+ // The user has cleared the selected file
+ }
+ // More logic to handle the file change
+}
+```
+
+다른 엔티티의 데이터 인풋을 감시하고 싶다면 `Watch(Entity otherEntity, string dataInputKey, Action onChange, bool deep = true)` 메서드를 사용할 수 있어요.
+
+:::tip
+감시자는 엔티티가 삭제될 때 자동으로 등록 해제돼요.
+:::
+
+## 에셋 상태 감시
+
+때로는 단순한 값 변경 이상의 것을 감시하고 싶을 때가 있어요. 예를 들어, 사용자가 새로운 `Character`를 선택할 때마다 메시지를 출력하는 간단한 에셋을 고려해보면:
+
+```csharp
+[DataInput]
+public CharacterAsset Character;
+
+protected override void OnCreate() {
+ base.OnCreate();
+ Watch(nameof(Character), OnCharacterChanged);
+}
+
+protected void OnCharacterChanged(CharacterAsset from, CharacterAsset to) {
+ if (to != null) Debug.Log("New character file selected! " + to.Source);
+}
+```
+
+하지만 이 감시자는 `Character` 데이터 인풋의 값이 변경될 때만 트리거됩니다. 즉, 씬에 캐릭터 에셋이 두 개 있을 경우, 사용자 간의 캐릭터 전환 시 트리거돼요. 하지만 씬에 캐릭터 에셋이 하나만 있고, 사용자가 해당 에셋 안에서 `Source` 데이터를 변경하면 이 감시자는 트리거되지 않아요.
+
+이런 경우, `WatchAsset`을 사용할 수 있어요:
+
+```csharp
+protected override void OnCreate() {
+ base.OnCreate();
+ WatchAsset(nameof(Character), OnCharacterChanged);
+}
+
+protected void OnCharacterChanged() {
+ if (Character.IsNonNullAndActive()) Debug.Log("New character file selected! " + Character.Source);
+}
+```
+
+이제 감시자는 `Character` 값이 변경될 때뿐만 아니라, 해당 `Character` 에셋의 `Source` 데이터가 변경될 때도 트리거됩니다.
+
+:::info
+왜 이 방식이 동작할까요? `WatchAsset` 메서드는 에셋의 [활성 상태](assets#active-state)가 변경될 때도 감시자를 트리거해요. Warudo에서는 `Source` 데이터 인풋을 가진 모든 에셋이 `Source`에 따라 활성 상태를 토글해요. 예를 들어, `CharacterAsset`은 `Source`를 사용해 .vrm 모델 또는 .warudo 캐릭터 모드를 로드하고 `PropAsset`은 `Source`를 사용해 .warudo 소품 모드를 로드하는 방식이에요. `Source`가 성공적으로 로드될 때만 활성 상태가 `true`가 돼요.
+
+예를 들어, 현재 활성화된 에셋이 있고, `Source`가 선택되어 있을 때 사용자가 새로운 `Source`를 선택하면 활성 상태는 `false`로 설정되고(감시자가 트리거됨), 새 `Source`가 성공적으로 로드되면 다시 `true`로 설정돼요(감시자가 다시 트리거됨). 위 코드에서는 두 번째 트리거에서만 메시지가 출력되는데, 첫 번째 트리거가 호출될 때는 캐릭터 에셋이 아직 활성화되지 않았기 때문이에요.
+:::
+
+## 다중 데이터 인풋 감시
+
+여러 데이터 인풋을 감시해야 하고, 그 중 하나라도 변경될 때 콜백 함수를 실행하고 싶다면 `WatchAll` 메서드를 사용할 수 있어요:
+
+```csharp
+protected override void OnCreate() {
+ base.OnCreate();
+ WatchAll(new [] { nameof(A), nameof(B), nameof(C) }, OnAnyDataInputChanged);
+}
+
+protected void OnAnyDataInputChanged() {
+ // Called when A, B, or C changes
+}
+```
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-plugin-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-plugin-mod.md
new file mode 100644
index 0000000..a49a0f5
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-plugin-mod.md
@@ -0,0 +1,197 @@
+---
+sidebar_position: 2
+---
+
+# 당신의 첫 플러그인 모드를 만들어보세요
+
+지난 튜토리얼에서는 [Playground](playground)를 사용해 커스텀 노드와 에셋을 로드했어요. 하지만 Playground는 몇 가지 한계가 있어요: 예를 들어, 유니티 에셋을 참조할 수 없기 때문에, 커스텀 유니티 파티클 프리팹을 스폰하는 노드를 만들고 싶다면 Playground만으로는 해결할 수 없어요.
+
+이번 튜토리얼에서는 [Warudo SDK](../modding/mod-sdk)를 사용해 우리가 만든 `HelloWorldNode`와 `CookieClickerAsset`을 포함하는 플러그인 모드를 빌드할 거예요. 플러그인 모드는 유니티 에셋을 저장하고 참조할 수 있을 뿐만 아니라, 다른 사용자와 커스텀 노드 및 에셋을 공유할 수도 있어요.
+
+## 1단계: Warudo SDK 프로젝트 생성
+
+아직 [Warudo SDK 설치](../modding/sdk-installation) 가이드를 따라 새로운 Warudo SDK 프로젝트를 만들지 않았다면 만들어 주세요. 먼저 [당신의 첫 모드를 만들어보세요](../modding/creating-your-first-mod) 튜토리얼을 따라 Warudo SDK에 익숙해지는 것을 권장해요.
+
+:::tip
+플러그인 모드를 빌드하는 과정은 다른 종류의 모드를 빌드하는 것과 매우 비슷해요! 유일한 차이점은, 플러그인 모드가 `Plugin` 클래스를 상속하는 C# 스크립트를 포함해야 한다는 점이에요.
+:::
+
+## 2단계: 새 모드 만들기
+
+새 모드를 만들려면, 메뉴 바에서 **Warudo → New Mod**를 선택하세요:
+
+![](/doc-img/en-mod-sdk-3.webp)
+
+**Mod Name**에 "HelloWorldPlugin"을 입력하고, Create Mod! 버튼을 클릭하세요:
+
+![](/doc-img/en-plugin-mod-1.png)
+
+Assets 폴더 하위에 모드를 위한 폴더가 생성된 것을 확인할 수 있을 거예요.
+
+## 3단계: 플러그인 스크립트 만들기
+
+"HelloWorldPlugin" 모드 폴더에서 마우스 오른쪽 클릭 후, 새 C# 스크립트를 만들어 `HelloWorldPlugin.cs`라고 이름을 지정하세요. 스크립트에 아래 코드를 붙여넣으세요:
+
+```csharp
+using UnityEngine;
+using Warudo.Core.Attributes;
+using Warudo.Core.Plugins;
+
+[PluginType(
+ Id = "hakuyatira.helloworld",
+ Name = "Hello World",
+ Description = "A simple plugin that says hello to the world.",
+ Version = "1.0.0",
+ Author = "Hakuya Tira",
+ SupportUrl = "https://docs.warudo.app",
+ AssetTypes = new [] { typeof(CookieClickerAsset) },
+ NodeTypes = new [] { typeof(HelloWorldNode) })]
+public class HelloWorldPlugin : Plugin {
+
+ protected override void OnCreate() {
+ base.OnCreate();
+ Debug.Log("The Hello World plugin is officially enabled! Hooray!");
+ }
+
+}
+```
+
+[이전 튜토리얼](creating-your-first-script)에서 사용했던 `HelloWorldNode.cs`와 `CookieClickerAsset.cs` 파일을 "HelloWorldPlugin" 모드 폴더에 복사하세요.
+
+이제 모드 폴더는 다음과 같이 보여야 합니다:
+
+
+
+
+
+
+
+## 4단계: 플러그인 모드 익스포트
+
+거의 완료되었어요! 모드를 익스포트하기 전에 **Warudo → Mod Settings**에서 모드 설정이 올바른지 확인하세요. 모드의 이름, 버전, 저자, 설명 등을 설정할 수 있는데, 이는 `[PluginType]` 파라미터와 동일해야 해요.
+
+기본적으로 **Mod Export Directory**는 비어 있는데, 이 경우 모드는 프로젝트의 루트 폴더로 익스포트돼요. 대신 Warudo의 데이터 폴더 내 `Plugins` 디렉토리로 설정할 수 있어요. 예를 들어, `C:\Program Files (x86)\Steam\steamapps\common\Warudo\Warudo_Data\StreamingAssets\Plugins` 경로로 설정할 수 있어요..
+
+:::warning
+익스포트하기 전에 Warudo를 닫고 `Playground` 디렉토리에서 `HelloWorldNode.cs`와 `CookieClickerAsset.cs` 파일을 삭제하세요. 이는 Playground와 플러그인 모드 간의 충돌을 방지하기 위함이에요.
+:::
+
+**Warudo → Export Mod** 를 선택해 플러그인 모드를 익스포트하세요. 모든 것이 제대로 진행되면 콘솔에 `BUILD SUCCEEDED!` 메시지가 표시될 거예요:
+
+![](/doc-img/en-plugin-mod-4.png)
+
+모드 폴더의 스크립트가 실제로 플러그인 모드로 컴파일되었는지 확인하는 것이 좋아요. 콘솔을 스크롤하여 아래와 같은 라인을 찾으세요:
+
+![](/doc-img/en-plugin-mod-5.png)
+
+:::warning
+이 라인이 보이지 않으면, Unity 프로젝트가 C# 스크립팅을 위해 올바르게 설정되지 않았을 수 있어요. [이 섹션](../modding/mod-sdk#custom-scripts)의 단계를 다시 따라 플러그인 모드를 익스포트하세요.
+:::
+
+익스포트된 `HelloWorldPlugin.warudo` 폴더가 Warudo의 데이터 폴더 내 `Plugins` 디렉토리에 있는지 확인하세요. 이제 Warudo를 열고 About 대화 상자에서 플러그인 모드가 로드되었는지 확인할 수 있어요:
+
+![](/doc-img/en-plugin-mod-3.png)
+
+그리고 물론 Cookie Clicker 에셋이 **Add Asset** 메뉴에, Hello World 노드가 노드 팔레트에 표시될 거예요:
+
+![](/doc-img/en-getting-started-playground-7.png)
+
+![](/doc-img/en-getting-started-playground-2.png)
+
+짜잔! Warudo에서의 첫 플러그인 모드 완성입니다!
+
+## 5단계: Unity 에셋 로드하기
+
+이제 플러그인 모드를 만들었으니, Warudo에 커스텀 Unity 에셋을 로드할 수 있어요! 이번에는 `CookieClickerAsset` 스크립트에서 Unity 에셋을 로드해볼 거예요.
+
+먼저 Unity Asset Store에서 [Cartoon FX Remaster Free](https://assetstore.unity.com/packages/vfx/particles/cartoon-fx-remaster-free-109565)를 다운로드하고, Unity 프로젝트에 임포트하세요. 패키지에서 파티클 프리팹을 선택한 후, **Ctrl** 키를 누른 상태로 "HelloWorldPlugin" 폴더에 드래그하세요. 이번 예제에서는 "CFXR Explosion 1" 프리팹을 사용할 거예요.
+
+![](/doc-img/en-plugin-mod-6.png)
+
+프리팹 이름을 `Particle`로 변경하세요. 이제 모드 폴더는 아래와 같이 보일 거예요:
+
+![](/doc-img/en-plugin-mod-7.png)
+
+`CookieClickerAsset.cs` 스크립트를 열고, 아래 코드로 교체하세요:
+
+```csharp
+using System;
+using Cysharp.Threading.Tasks;
+using UnityEngine;
+using Warudo.Core.Attributes;
+using Warudo.Core.Scenes;
+using Object = UnityEngine.Object;
+using Random = UnityEngine.Random;
+
+[AssetType(Id = "82ae6c21-e202-4e0e-9183-318e2e607672", Title = "Cookie Clicker")]
+public class CookieClickerAsset : Asset {
+
+ [Markdown]
+ public string Status = "You don't have any cookies.";
+
+ [DataInput]
+ [IntegerSlider(1, 10)]
+ [Description("Increase me to get more cookies each time!")]
+ public int Multiplier = 1;
+
+ private int count;
+ private GameObject particlePrefab; // New field to store the particle prefab
+
+ [Trigger]
+ public async void GimmeCookie() { // Note the async keyword
+ count += Multiplier;
+ SetDataInput(nameof(Status), "You have " + count + " cookie(s).", broadcast: true);
+
+ // Spawn the particle prefab Multiplier times
+ for (var i = 0; i < Multiplier; i++) {
+ var particle = Object.Instantiate(particlePrefab, Random.insideUnitSphere * 2f, Quaternion.identity);
+ particle.SetActive(true);
+ Object.Destroy(particle, 3f); // Automatically destroy the cloned particle after 3 seconds
+
+ await UniTask.Delay(TimeSpan.FromSeconds(0.2f)); // Delay 0.2 seconds before spawning the next particle
+ }
+ }
+
+ protected override void OnCreate() {
+ base.OnCreate();
+ SetActive(true);
+
+ // Load the particle prefab from the mod folder. Change this path if your prefab is in a different folder
+ particlePrefab = Plugin.ModHost.Assets.Instantiate("Assets/HelloWorldPlugin/Particle.prefab");
+ // Disable it so that it doesn't show up in the scene
+ particlePrefab.SetActive(false);
+ }
+
+}
+```
+
+모드를 다시 익스포트하세요. Warudo는 플러그인의 핫 리로딩을 지원하므로 `Plugins` 폴더에 모드를 익스포트하면 Warudo에서 바로 변경 사항을 확인할 수 있어요!
+
+Cookie Clicker 에셋에서 "Gimme Cookie" 버튼을 누르면 파티클 프리팹이 씬에 생성되는 것을 볼 수 있을 거예요:
+
+![](/doc-img/en-plugin-mod-8.png)
+
+:::info
+Warudo Pro 사용자라면, 파티클 효과를 보기 위해 내장 렌더링 파이프라인으로 전환하세요. 또는 URP 호환 파티클 에셋을 사용할 수도 있어요.
+:::
+
+Warudo에서 이런 작업을 할 수 있다니, 정말 멋지지 않나요?
+
+## Playground와 비교
+
+플러그인 모드를 사용하는 것이 [Playground](playground)보다 강력하지만, 단점도 있어요:
+
+* 플러그인 모드를 개발할 때는 매번 변경 사항을 반영하기 위해 모드를 익스포트해야 하므로 속도가 느려질 수 있어요;
+* 현재 Playground는 MessagePack, WebSocketSharp 등 Warudo에서 사용하는 더 많은 라이브러리에 접근할 수 있어요 (플러그인 모드를 개선하기 위한 작업이 진행 중이에요);
+* 플러그인 모드는 더 많은 [보안 제한](plugin-mod#limitations)이 있어요. 예를 들어 `System.IO` 네임페이스에 접근할 수 없어요. (이를 대신할 수 있는 안전한 [sandboxed file persistence API](api/io)를 제공해요).
+
+플러그인 모드나 Playground 중 어떤 것을 사용할지는 용도에 따라 달라요. Unity 에셋을 참조할 필요가 없는 새로운 기능이나 아이디어를 프로토타입으로 제작해 테스트할 때는 Playground가 빠르게 결과를 확인할 수 있는 훌륭한 도구예요. VTuber를 위한 맞춤형 개발에서도 Playground는 클라이언트를 위한 작은 기능을 구현하는 데 적합해요. 하지만 커스텀 Unity 에셋을 로드하거나 [our Steam Workshop](../modding/sharing)에 커스텀 노드와 에셋을 배포하려면 플러그인 모드가 더 적합해요. (그래도 플러그인 모드로 옮기기 전에 Playground에서 커스텀 노드와 에셋을 먼저 프로토타입으로 제작해볼 수 있다는 점은 여전히 유용하죠!)
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-script.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-script.md
new file mode 100644
index 0000000..823447f
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/creating-your-first-script.md
@@ -0,0 +1,164 @@
+---
+sidebar_position: 1
+---
+
+# 당신의 첫 스크립트를 만들어보세요
+
+Warudo에서 스크립팅하는 방법은 두 가지가 있어요: **Playground**를 사용하거나 [플러그인 모드](distribution.md)를 만드는 것이죠. 이번 튜토리얼에서는 Playground를 사용해 첫 번째 커스텀 노드를 작성하는 방법을 살펴볼 거예요!
+
+:::tip
+**playground란?** Playground는 일종의 샌드박스로, 커스텀 노드와 에셋을 작성하고 테스트할 수 있는 공간이라고 생각하면 돼요. C# 코드를 컴파일하고 패키징하는 대신, Warudo 데이터 폴더의 `Playground` 디렉토리에서 바로 코드를 작성할 수 있어요. 이렇게 하면 모드를 빌드하거나 Warudo를 재시작하지 않고도 빠르게 코드를 반복해서 테스트할 수 있죠.
+:::
+
+자, 그럼 바로 시작해볼까요?
+
+:::info
+궁금한 점이 생기면, 언제든지 [디스코드 서버](https://discord.gg/warudo)의 **#plugins-scripting** 채널에서 도움을 요청할 수 있으니 참고하세요!
+:::
+
+## 1단계: 환경 설정
+
+먼저, [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0)가 설치되어 있는지 확인하세요. 그런 다음, 이 [링크](/scripts/Playground.csproj)에서 `.csproj` 파일을 다운로드하고 Warudo 데이터 폴더 내의 `Playground` 디렉토리에 넣으세요(Menu → Open Data Folder). 이 파일은 IDE에서 코드 자동 완성 및 구문 강조 표시를 제공하는 데 도움이 돼요.
+
+이제 선호하는 C# IDE를 열어주세요. 우리는 [JetBrains Rider](https://www.jetbrains.com/rider/)를 사용할 거지만 [Visual Studio Code](https://code.visualstudio.com/)와 같은 다른 IDE도 괜찮아요. (Visual Studio Code를 사용하는 경우 [C# 언어 확장](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp)을 설치해야 할 수 있어요.)
+
+IDE에서 `Playground` 디렉토리를 열고, `HelloWorldNode.cs`라는 파일을 생성한 뒤, 다음 코드를 파일에 붙여 넣으세요:
+
+```csharp
+using Warudo.Core;
+using Warudo.Core.Attributes;
+using Warudo.Core.Graphs;
+
+[NodeType(Id = "c76b2fef-a7e7-4299-b942-e0b6dec52660", Title = "Hello World")]
+public class HelloWorldNode : Node {
+
+ [FlowInput]
+ public Continuation Enter() {
+ Context.Service.PromptMessage("Hello World!", "This node is working!");
+ return Exit;
+ }
+
+ [FlowOutput]
+ public Continuation Exit;
+
+}
+```
+
+## 2단계: 커스텀 노드
+
+Warudo를 열면, 시작 시 아래와 같은 팝업 메시지(토스트)가 나타날 거예요:
+
+![](/doc-img/en-getting-started-playground-1.png)
+
+이 메시지는 `HelloWorldNode.cs`가 성공적으로 컴파일되고 로드되었음을 나타내요. 또한, 코드에서 커스텀 노드를 정의했으므로, 메시지에 `Nodes: 1`이라고 표시되는 것을 확인할 수 있어요.
+
+blueprint 탭으로 이동해서 새로운 blueprint를 만들고, 노드 팔레트에서 `Hello World` 노드를 찾아서 드래그하세요. 아래와 같은 화면이 보일 거예요:
+
+![](/doc-img/en-getting-started-playground-2.png)
+
+`Enter` 플로우 인풋을 클릭하면 다음과 같은 메시지가 나타날 거예요:
+
+![](/doc-img/en-getting-started-playground-3.png)
+
+모두 제대로 작동한다면, 축하해요! 여러분은 방금 첫 번째 Warudo 노드를 만들었어요!
+
+## 3단계: 핫 리로드
+
+한가지 놀라운 점은 코드를 수정할 때마다 Warudo를 재시작할 필요가 없다는 거예요. 실제로 Warudo는 `Playground` 디렉토리에서 변경 사항을 자동으로 감지하고 코드를 핫 리로드해요!
+
+그럼, 이를 직접 확인해 볼게요. `HelloWorldNode.cs` 파일로 돌아가 아래 새로운 코드를 붙여넣어 주세요. (직접 타이핑을 연습하고 싶다면 기존 코드를 업데이트해도 돼요.):
+
+```csharp
+using Warudo.Core;
+using Warudo.Core.Attributes;
+using Warudo.Core.Graphs;
+
+[NodeType(Id = "c76b2fef-a7e7-4299-b942-e0b6dec52660", Title = "Hello World")]
+public class HelloWorldNode : Node {
+
+ // New code below
+ [DataInput]
+ [IntegerSlider(1, 100)]
+ public int LuckyNumber = 42;
+ // New code above
+
+ [FlowInput]
+ public Continuation Enter() {
+ // Changed code below
+ Context.Service.PromptMessage("Hello World!", "This node is working! My lucky number: " + LuckyNumber);
+ return Exit;
+ }
+
+ [FlowOutput]
+ public Continuation Exit;
+
+}
+```
+
+Ctrl+S를 누르자마자, 또 다른 토스트 메시지가 나타나고, 노드가 성공적으로 컴파일되고 핫 리로드되었음을 알릴 거예요. blueprint에 있는 노드도 자동으로 업데이트됩니다:
+
+![](/doc-img/en-getting-started-playground-4.png)
+
+이제 행운의 숫자를 설정하고, 노드가 여전히 제대로 작동하는지 확인해 보세요!
+
+![](/doc-img/en-getting-started-playground-5.png)
+
+## 4단계: 커스텀 에셋
+
+Warudo는 새로운 `.cs` 파일도 감지할 수 있어요. 이번에는 커스텀 에셋을 만들어 볼게요. `Playground` 디렉토리에서 `CookieClickerAsset.cs`라는 파일을 생성하고, 아래 코드를 붙여넣어 주세요:
+
+```csharp
+using Warudo.Core.Attributes;
+using Warudo.Core.Scenes;
+
+[AssetType(Id = "82ae6c21-e202-4e0e-9183-318e2e607672", Title = "Cookie Clicker")]
+public class CookieClickerAsset : Asset {
+
+ [Markdown]
+ public string Status = "You don't have any cookies.";
+
+ [DataInput]
+ [IntegerSlider(1, 10)]
+ [Description("Increase me to get more cookies each time!")]
+ public int Multiplier = 1;
+
+ private int count;
+
+ [Trigger]
+ public void GimmeCookie() {
+ count += Multiplier;
+ SetDataInput(nameof(Status), "You have " + count + " cookie(s).", broadcast: true);
+ }
+
+ protected override void OnCreate() {
+ base.OnCreate();
+ SetActive(true);
+ }
+
+}
+```
+
+해당 코드를 저장하면, 커스텀 노드 (`HelloWorldNode.cs`)와 커스텀 에셋 (`CookieClickerAsset.cs`)이 로드되었다는 토스트 메시지가 나타날 거예요:
+
+![](/doc-img/en-getting-started-playground-6.png)
+
+새로운 에셋은 **Add Asset** 메뉴에서 찾을 수 있어요:
+
+![](/doc-img/en-getting-started-playground-7.png)
+
+씬에 추가하고 즐겨보세요! VTubing이 필요 없을 정도로 재밌을 거예요!
+
+![](/doc-img/en-getting-started-playground-8.png)
+
+## Summary
+
+이번 짧은 튜토리얼에서는 Playground를 사용하는 방법과 커스텀 노드 및 에셋의 기본 개념을 배웠어요. 다음 튜토리얼에서는 우리가 만든 커스텀 노드와 에셋을 배포하기 위한 플러그인 모드를 만드는 방법을 배울 거예요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/debugging.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/debugging.md
new file mode 100644
index 0000000..58bb826
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/debugging.md
@@ -0,0 +1,36 @@
+---
+sidebar_position: 1000
+---
+
+# 디버깅
+
+스크립트가 의도한 대로 작동하지 않나요? 여기 몇 가지 디버깅 팁이 있어요!
+
+## Warudo 재시작
+
+진부하게 들릴 수 있지만, Warudo를 재시작하면 많은 문제를 해결할 수 있어요. 특히, 모드나 스크립트가 핫 리로드될 때 문제가 발생할 수 있는데, 이럴 때는 먼저 재시작을 시도해 보세요.
+
+## 로깅
+
+Unity의 `Debug.Log`를 사용해서 콘솔에 메시지를 출력할 수 있어요. 로그 폴더는 **Menu → Open Logs Folder**를 통해 접근할 수 있어요. 현재 세션의 로그는 `Player.log` 파일에, 이전 세션의 로그는 `Player-prev.log` 파일에 기록돼요.
+
+## 전체 빌드 로그 활성화
+
+ [플러그인 모드](plugin-mod)를 만들고 있다면, Mod Settings 창에서 **Log Level**을 All로 설정하고 **Clear Console On Build**옵션을 해제하는 것을 추천해요:
+
+![](/doc-img/en-mod-13.png)
+
+[모드](../modding/mod-sdk#custom-scripts) 제작 문서도 참고해 보세요.
+
+## 디스코드 참여하기
+
+그래도 문제가 해결되지 않으면 [Discord](https://discord.gg/warudo)에서 도움을 요청하세요. **#plugins-scripting** 채널에서 스크립팅 관련 도움을 받을 수 있고, 경험 많은 분들이 문제 진단을 도와줄 거예요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/overview.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/overview.md
new file mode 100644
index 0000000..2f7d395
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/overview.md
@@ -0,0 +1,69 @@
+---
+sidebar_position: 0
+---
+
+# 개요
+
+Warudo의 스크립팅 시스템을 사용하면 C# 코드를 작성해 새로운 [에셋](../assets/overview)과 [노드](../blueprints/overview)를 VTubing 설정에 추가하여 Warudo의 기능을 확장할 수 있어요. [plugin mod](plugin-mod)를 만들어 Steam 워크샵에 여러분의 커스텀 에셋과 노드를 공유할 수 있어요.
+
+다음은 Warudo 커뮤니티에서 제작한 몇 가지 멋진 플러그인들이에요:
+
+* [Feline's Headpat Node](https://steamcommunity.com/sharedfiles/filedetails/?id=3010238299&searchtext=)
+* [Input Receiver and Tools for Mouse/Pen/Gamepad](https://steamcommunity.com/sharedfiles/filedetails/?id=3221461980&searchtext=)
+* [Emotes From Twitch Message + Emote Dropper](https://steamcommunity.com/sharedfiles/filedetails/?id=3070622133&searchtext=)
+* [Rainbow Color Node](https://steamcommunity.com/sharedfiles/filedetails/?id=3016521495&searchtext=)
+* [Streamer.bot integration](https://steamcommunity.com/sharedfiles/filedetails/?id=3260939914&searchtext=)
+
+:::tip
+**Warudo는 기본적으로 커스텀 스크립팅**을 염두에 두고 제작되었어요. 사실 Warudo의 모든 기능은 여러분이 커스텀 노드, 에셋, 플러그인을 만들 때 사용하는 API(`Warudo.Core` 네임스페이스)를 기반으로 만들어졌어요.
+
+예를 들어, Warudo의 Stream Deck 통합 기능은 실제로 내장된 플러그인으로 이 플러그인의 소스 코드를 [여기](https://github.com/HakuyaLabs/WarudoPluginExamples) 참조용으로 공개해 두었어요.
+:::
+
+Warudo의 스크립팅 시스템이 어떻게 작동하는지 간단히 맛보기로, 트리거될 때 캐릭터에 무작위 표정을 재생하는 커스텀 노드 예시를 보여드릴게요:
+
+![](/doc-img/en-scripting-overview.png)
+
+그리고 이에 해당하는 C# 코드:
+
+```csharp
+using UnityEngine;
+using Warudo.Core.Attributes;
+using Warudo.Core.Graphs;
+using Warudo.Plugins.Core.Assets.Character;
+
+// Define a custom node type that will be shown in the note palette
+[NodeType(Id = "95cd88ae-bebe-4dc0-b52b-ba94799f08e9", Title = "Character Play Random Expression")]
+public class CharacterPlayRandomExpressionNode : Node {
+
+ [DataInput]
+ public CharacterAsset Character; // Let the user select a character
+
+ [FlowInput]
+ public Continuation Enter() { // When the node is triggered via the "Enter" flow input
+ if (Character.Expressions.Length == 0) return Exit; // If the character has no expressions, exit
+
+ Character.ExitAllExpressions(); // Exit all current expressions
+
+ var randomExpression = Character.Expressions[Random.Range(0, Character.Expressions.Length)];
+ Character.EnterExpression(randomExpression.Name, transient: false); // Play a random expression
+
+ return Exit; // Continue the flow and trigger whatever connected to the "Exit" flow output
+ }
+
+ [FlowOutput]
+ public Continuation Exit;
+
+}
+```
+
+흥미로워 보이나요? 계속 읽어보세요!
+
+
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/playground.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/playground.md
new file mode 100644
index 0000000..1c89677
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/playground.md
@@ -0,0 +1,46 @@
+---
+sidebar_position: 50
+---
+
+# 플레이그라운드
+
+Playground는 Warudo에서 [플러그인 모드](plugin-mod)를 만들지 않고도 커스텀 스크립트를 로드할 수 있는 개발 환경이에요. `Playground` 폴더에 넣은 C# 스크립트(`.cs` 파일)는 Warudo에서 자동으로 컴파일 및 로드되며, 스크립트에 변경 사항이 생기면 자동으로 다시 로드돼요.
+
+:::tip
+Playground를 처음 사용하는 방법에 대해서는 [당신의 첫 스크립트를 만들어보세요](creating-your-first-script)를 확인해 보세요!
+:::
+
+Playground에는 어떤 C# 스크립트든 넣을 수 있지만, Warudo가 이를 인식하려면 적어도 하나의 클래스는 `Node`, `Asset` 또는 `Plugin` 중 하나의 [엔티티 유형](api/entities.md)을 상속해야 해요.
+
+## 환경 설정
+
+Playground를 사용하기 위해 IDE가 꼭 필요한 건 아니지만, 코드 완성과 구문 강조를 위해 IDE 사용을 권장해요. [JetBrains Rider](https://www.jetbrains.com/rider/)나 [Visual Studio Code](https://code.visualstudio.com/)를 추천해요..
+
+그 다음, `.csproj` 파일을 [여기](/scripts/Playground.csproj)에서 다운로드해 Warudo 데이터 폴더 내 `Playground` 디렉터리에 넣어주세요(Menu → Open Data Folder). 이 파일을 IDE로 열면 준비가 완료돼요!
+
+## 엔티티 핫리로딩
+
+핫리로딩은 엔티티 데이터를 직렬화하고, 기존 엔티티 타입을 언로드한 후 새로운 엔티티 타입을 로드 및 인스턴스화한 다음, 기존 엔티티 데이터를 다시 역직렬화하는 방식으로 작동해요. 이 과정은 보통 몇 초 안에 완료돼요.
+
+핫리로딩이 완료되면, 몇 개의 에셋, 노드 또는 플러그인이 다시 로드되었는지 나타내는 토스트 메시지가 표시될 거예요.
+
+:::tip
+오류가 발생하면, 토스트 메시지를 클릭해 컴파일 오류를 확인할 수 있어요.
+:::
+
+## 제한 사항
+
+Playground에는 다음과 같은 제한이 있어요:
+
+* 서드파티 DLLs 또는 NuGet 패키지를 쉽게 사용할 수 없어요. 이 부분은 앞으로 개선할 예정이에요.
+* Unity 에셋(예: 프리팹, 재질, 텍스처)을 Playground 스크립트에서 추가하거나 참조할 수 없어요([플러그인 모드](plugin-mod)를 사용하세요).
+
+
\ No newline at end of file
diff --git a/i18n/ko/docusaurus-plugin-content-docs/current/scripting/plugin-mod.md b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/plugin-mod.md
new file mode 100644
index 0000000..07dda9a
--- /dev/null
+++ b/i18n/ko/docusaurus-plugin-content-docs/current/scripting/plugin-mod.md
@@ -0,0 +1,48 @@
+---
+sidebar_position: 100
+---
+
+# 플러그인 모드
+
+플러그인 모드는 Warudo에 커스텀 에셋과 노드를 추가하는 유형의 [Warudo 모드](../modding/mod-sdk)에요.
+
+:::tip
+플러그인 모드를 만들어보려면 [당신의 첫 플러그인 모드를 만들어보세요](creating-your-first-plugin-mod) 튜토리얼을 참고하세요!
+:::
+
+:::info
+이 페이지에서는 플러그인 모드를 만드는 일반적인 가이드를 다루고 있어요. 추가로 [플러그인](api/plugins) 스크립팅 API 페이지도 참고해 주세요.
+:::
+
+## 환경 설정
+
+플러그인 모드는 `Plugin`을 상속하는 C# 스크립트가 포함된 일반적인 모드예요. [당신의 첫 모드를 만들어보세요](../modding/creating-your-first-mod)을 따라 새로운 모드를 만든 후, 모드 폴더에 C# 스크립트를 생성하고 `Plugin` 상속해 주세요. 커스텀 에셋이나 노드 타입이 있다면 `[PluginType]` 속성의 `AssetTypes` 및 `NodeTypes` 속성에 해당 타입들이 등록되었는지 확인해야 해요(자세한 내용은 [플러그인 API](api/plugins)를 참고해 주세요).
+
+## Unity 에셋 포함하기
+
+프리팹, 재질, 텍스처와 같은 Unity 에셋을 플러그인 모드에 포함하려면 에셋을 모드 폴더에 넣으면 돼요. Warudo는 모드를 익스포트할 때 이러한 에셋을 자동으로 포함해요.
+
+:::caution
+프리팹이나 재질에 커스텀 셰이더나 스크립트가 사용된 경우, 해당 셰이더나 스크립트도 모드 폴더에 포함해 주세요.
+:::
+
+런타임 중에 이러한 에셋을 스크립트에서 로드할 수 있어요. 자세한 내용은 [로딩 Unity 에셋](api/plugins#loading-unity-assets)을 참고해 주세요.
+
+## 제한 사항 {#limitations}
+
+플러그인 모드는 다음과 같은 제한이 있어요:
+
+- 서드파티 DLLs이나 NuGet 패키지를 사용할 수 없어요.
+- 어셈블리의 일부인 C# 스크립트를 익스포트할 수 없어요. 즉, 모드 폴더에 `.asmdef` 파일을 포함할 수 없어요.
+- 리플렉션(`System.Reflection`)을 사용할 수 없고, 파일 시스템(`System.IO`)에 접근할 수 없어요.
+ * `MonoBehaviour` 컴포넌트의 메서드를 호출하려면 Unity의 [`SendMessage`](https://docs.unity3d.com/ScriptReference/GameObject.SendMessage.html) 또는 [`BroadcastMessage`](https://docs.unity3d.com/ScriptReference/Component.BroadcastMessage.html) 메서드를 사용하는 것을 권장해요.
+ * 데이터를 저장하고 불러오는 방법은 [데이터 저장 및 로딩](api/io)을 참고하세요.
+
+