diff --git a/Assets/Scripts/Editor/PlaygroundSetup.cs b/Assets/Scripts/Editor/PlaygroundSetup.cs
new file mode 100644
index 0000000..cc6d583
--- /dev/null
+++ b/Assets/Scripts/Editor/PlaygroundSetup.cs
@@ -0,0 +1,81 @@
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using UnityEngine;
+using SinoWms.Player;
+
+namespace SinoWms.EditorTools
+{
+ ///
+ /// 캐릭터 이동 확인용 최소 플레이그라운드를 구성한다.
+ /// 메뉴 SinoWMS/Setup Playground Scene 실행 후 Play → WASD/방향키로 이동.
+ ///
+ public static class PlaygroundSetup
+ {
+ [MenuItem("SinoWMS/Setup Playground Scene")]
+ public static void Setup()
+ {
+ // 바닥
+ var ground = GameObject.Find("Ground");
+ if (ground == null)
+ {
+ ground = GameObject.CreatePrimitive(PrimitiveType.Plane);
+ ground.name = "Ground";
+ ground.transform.localScale = new Vector3(5f, 1f, 5f); // 50x50
+ ground.GetComponent().sharedMaterial = MakeMaterial(new Color(0.5f, 0.55f, 0.6f));
+ }
+
+ // 플레이어
+ var player = GameObject.Find("Player");
+ if (player == null)
+ {
+ player = GameObject.CreatePrimitive(PrimitiveType.Capsule);
+ player.name = "Player";
+ player.transform.position = new Vector3(0f, 1.1f, 0f);
+
+ // CharacterController와 기본 CapsuleCollider 충돌 방지
+ var capCol = player.GetComponent();
+ if (capCol != null) Object.DestroyImmediate(capCol);
+
+ var cc = player.AddComponent();
+ cc.height = 2f;
+ cc.radius = 0.5f;
+ cc.center = Vector3.zero;
+
+ player.AddComponent();
+ player.GetComponent().sharedMaterial = MakeMaterial(new Color(0.2f, 0.5f, 1f));
+
+ // 정면 표시용 작은 큐브(어느 쪽을 보는지)
+ var nose = GameObject.CreatePrimitive(PrimitiveType.Cube);
+ nose.name = "Nose";
+ Object.DestroyImmediate(nose.GetComponent());
+ nose.transform.SetParent(player.transform, false);
+ nose.transform.localScale = new Vector3(0.25f, 0.25f, 0.4f);
+ nose.transform.localPosition = new Vector3(0f, 0.3f, 0.5f);
+ nose.GetComponent().sharedMaterial = MakeMaterial(new Color(1f, 0.85f, 0.3f));
+ }
+
+ // 카메라 (탑다운 추적)
+ var cam = Camera.main;
+ if (cam == null)
+ {
+ var camGo = new GameObject("Main Camera") { tag = "MainCamera" };
+ cam = camGo.AddComponent();
+ }
+ var follow = cam.GetComponent() ?? cam.gameObject.AddComponent();
+ follow.SetTarget(player.transform);
+ cam.transform.position = player.transform.position + new Vector3(0f, 12f, -8f);
+ cam.transform.LookAt(player.transform.position);
+
+ Selection.activeGameObject = player;
+ EditorSceneManager.MarkSceneDirty(player.scene);
+ Debug.Log("[PlaygroundSetup] 완료. Play 후 WASD/방향키로 캐릭터를 움직여 보세요.");
+ }
+
+ /// URP 환경에서 분홍색으로 안 보이도록 URP/Lit 머티리얼 생성.
+ private static Material MakeMaterial(Color color)
+ {
+ var shader = Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard");
+ return new Material(shader) { color = color };
+ }
+ }
+}
diff --git a/Assets/Scripts/Editor/PlaygroundSetup.cs.meta b/Assets/Scripts/Editor/PlaygroundSetup.cs.meta
new file mode 100644
index 0000000..d0a89f4
--- /dev/null
+++ b/Assets/Scripts/Editor/PlaygroundSetup.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 6a6e35879fd6146759034c5dab7c4afd
\ No newline at end of file
diff --git a/Assets/Scripts/Player.meta b/Assets/Scripts/Player.meta
new file mode 100644
index 0000000..d8afb3e
--- /dev/null
+++ b/Assets/Scripts/Player.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7891d41cf22544f878fa7dee0c7cc07a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Scripts/Player/CameraFollow.cs b/Assets/Scripts/Player/CameraFollow.cs
new file mode 100644
index 0000000..d3fa4de
--- /dev/null
+++ b/Assets/Scripts/Player/CameraFollow.cs
@@ -0,0 +1,23 @@
+using UnityEngine;
+
+namespace SinoWms.Player
+{
+ /// 대상(캐릭터)을 위에서 비스듬히 따라다니는 탑다운 카메라.
+ public class CameraFollow : MonoBehaviour
+ {
+ [SerializeField] private Transform target;
+ [SerializeField] private Vector3 offset = new Vector3(0f, 12f, -8f);
+ [SerializeField] private float smooth = 8f;
+
+ public void SetTarget(Transform t) => target = t;
+
+ private void LateUpdate()
+ {
+ if (target == null) return;
+
+ Vector3 desired = target.position + offset;
+ transform.position = Vector3.Lerp(transform.position, desired, smooth * Time.deltaTime);
+ transform.LookAt(target.position);
+ }
+ }
+}
diff --git a/Assets/Scripts/Player/CameraFollow.cs.meta b/Assets/Scripts/Player/CameraFollow.cs.meta
new file mode 100644
index 0000000..c8e0737
--- /dev/null
+++ b/Assets/Scripts/Player/CameraFollow.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 378ab13c02a4c4074878e994d1593434
\ No newline at end of file
diff --git a/Assets/Scripts/Player/PlayerController.cs b/Assets/Scripts/Player/PlayerController.cs
new file mode 100644
index 0000000..2eb9964
--- /dev/null
+++ b/Assets/Scripts/Player/PlayerController.cs
@@ -0,0 +1,55 @@
+using UnityEngine;
+using UnityEngine.InputSystem;
+
+namespace SinoWms.Player
+{
+ ///
+ /// 탑다운 캐릭터 이동. 새 Input System(Keyboard.current)으로 WASD/방향키 입력을 받아
+ /// XZ 평면을 이동하고, 진행 방향으로 부드럽게 회전한다.
+ ///
+ [RequireComponent(typeof(CharacterController))]
+ public class PlayerController : MonoBehaviour
+ {
+ [SerializeField] private float moveSpeed = 5f;
+ [SerializeField] private float turnSpeed = 12f;
+ [SerializeField] private float gravity = -20f;
+
+ private CharacterController _cc;
+ private float _verticalVelocity;
+
+ private void Awake()
+ {
+ _cc = GetComponent();
+ }
+
+ private void Update()
+ {
+ var kb = Keyboard.current;
+ if (kb == null) return;
+
+ Vector2 input = Vector2.zero;
+ if (kb.wKey.isPressed || kb.upArrowKey.isPressed) input.y += 1f;
+ if (kb.sKey.isPressed || kb.downArrowKey.isPressed) input.y -= 1f;
+ if (kb.dKey.isPressed || kb.rightArrowKey.isPressed) input.x += 1f;
+ if (kb.aKey.isPressed || kb.leftArrowKey.isPressed) input.x -= 1f;
+
+ Vector3 move = new Vector3(input.x, 0f, input.y);
+ if (move.sqrMagnitude > 1f) move.Normalize();
+
+ // 중력 (바닥에 붙어있게)
+ if (_cc.isGrounded && _verticalVelocity < 0f) _verticalVelocity = -2f;
+ _verticalVelocity += gravity * Time.deltaTime;
+
+ Vector3 velocity = move * moveSpeed;
+ velocity.y = _verticalVelocity;
+ _cc.Move(velocity * Time.deltaTime);
+
+ // 진행 방향으로 회전
+ if (move.sqrMagnitude > 0.01f)
+ {
+ Quaternion target = Quaternion.LookRotation(move, Vector3.up);
+ transform.rotation = Quaternion.Slerp(transform.rotation, target, turnSpeed * Time.deltaTime);
+ }
+ }
+ }
+}
diff --git a/Assets/Scripts/Player/PlayerController.cs.meta b/Assets/Scripts/Player/PlayerController.cs.meta
new file mode 100644
index 0000000..88274ff
--- /dev/null
+++ b/Assets/Scripts/Player/PlayerController.cs.meta
@@ -0,0 +1,2 @@
+fileFormatVersion: 2
+guid: 00269464ac30a416992064182bd7186c
\ No newline at end of file