feat: 탑다운 캐릭터 이동 플레이그라운드
- PlayerController: WASD/방향키 이동 + 진행방향 회전 (새 Input System) - CameraFollow: 위에서 비스듬히 따라가는 카메라 - PlaygroundSetup: 바닥+캐릭터+카메라 자동 구성 메뉴 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
using UnityEditor;
|
||||
using UnityEditor.SceneManagement;
|
||||
using UnityEngine;
|
||||
using SinoWms.Player;
|
||||
|
||||
namespace SinoWms.EditorTools
|
||||
{
|
||||
/// <summary>
|
||||
/// 캐릭터 이동 확인용 최소 플레이그라운드를 구성한다.
|
||||
/// 메뉴 <b>SinoWMS/Setup Playground Scene</b> 실행 후 Play → WASD/방향키로 이동.
|
||||
/// </summary>
|
||||
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<Renderer>().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<CapsuleCollider>();
|
||||
if (capCol != null) Object.DestroyImmediate(capCol);
|
||||
|
||||
var cc = player.AddComponent<CharacterController>();
|
||||
cc.height = 2f;
|
||||
cc.radius = 0.5f;
|
||||
cc.center = Vector3.zero;
|
||||
|
||||
player.AddComponent<PlayerController>();
|
||||
player.GetComponent<Renderer>().sharedMaterial = MakeMaterial(new Color(0.2f, 0.5f, 1f));
|
||||
|
||||
// 정면 표시용 작은 큐브(어느 쪽을 보는지)
|
||||
var nose = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
||||
nose.name = "Nose";
|
||||
Object.DestroyImmediate(nose.GetComponent<BoxCollider>());
|
||||
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<Renderer>().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<Camera>();
|
||||
}
|
||||
var follow = cam.GetComponent<CameraFollow>() ?? cam.gameObject.AddComponent<CameraFollow>();
|
||||
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/방향키로 캐릭터를 움직여 보세요.");
|
||||
}
|
||||
|
||||
/// <summary>URP 환경에서 분홍색으로 안 보이도록 URP/Lit 머티리얼 생성.</summary>
|
||||
private static Material MakeMaterial(Color color)
|
||||
{
|
||||
var shader = Shader.Find("Universal Render Pipeline/Lit") ?? Shader.Find("Standard");
|
||||
return new Material(shader) { color = color };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6a6e35879fd6146759034c5dab7c4afd
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7891d41cf22544f878fa7dee0c7cc07a
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace SinoWms.Player
|
||||
{
|
||||
/// <summary>대상(캐릭터)을 위에서 비스듬히 따라다니는 탑다운 카메라.</summary>
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 378ab13c02a4c4074878e994d1593434
|
||||
@@ -0,0 +1,55 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace SinoWms.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// 탑다운 캐릭터 이동. 새 Input System(Keyboard.current)으로 WASD/방향키 입력을 받아
|
||||
/// XZ 평면을 이동하고, 진행 방향으로 부드럽게 회전한다.
|
||||
/// </summary>
|
||||
[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<CharacterController>();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00269464ac30a416992064182bd7186c
|
||||
Reference in New Issue
Block a user