feat: 집기/놓기 상호작용 (게임 핵심 루프)
- Carryable: 집을 수 있는 물건 표시 - PickupController: 범위 내 박스를 E키로 집고/놓기 + 화면 힌트 - PickupDemoSetup: 플레이어에 PickupController 부착 + 입고 박스 4개 스폰 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace SinoWms.Gameplay
|
||||
{
|
||||
/// <summary>플레이어가 집어 옮길 수 있는 물건(박스/팔레트) 표시 컴포넌트.</summary>
|
||||
[RequireComponent(typeof(Rigidbody))]
|
||||
[RequireComponent(typeof(Collider))]
|
||||
public class Carryable : MonoBehaviour
|
||||
{
|
||||
[Tooltip("힌트/로그에 표시할 이름. 비우면 오브젝트 이름 사용.")]
|
||||
public string displayName;
|
||||
|
||||
public string Label => string.IsNullOrEmpty(displayName) ? name : displayName;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 315a7e23f649b4187a444b01f14a8e9d
|
||||
@@ -0,0 +1,99 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.InputSystem;
|
||||
using SinoWms.Gameplay;
|
||||
|
||||
namespace SinoWms.Player
|
||||
{
|
||||
/// <summary>
|
||||
/// 캐릭터의 집기/놓기 상호작용. 범위 내 가장 가까운 Carryable을 E키로 집어
|
||||
/// 앞쪽 홀드 포인트에 들고 다니고, 다시 E키로 내려놓는다.
|
||||
/// </summary>
|
||||
public class PickupController : MonoBehaviour
|
||||
{
|
||||
[SerializeField] private float pickupRange = 2.5f;
|
||||
[SerializeField] private Transform holdPoint;
|
||||
|
||||
private Carryable _carried;
|
||||
private Carryable _target; // 현재 집을 수 있는 후보(힌트 표시용)
|
||||
private GUIStyle _style;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (holdPoint == null)
|
||||
{
|
||||
var hp = new GameObject("HoldPoint").transform;
|
||||
hp.SetParent(transform, false);
|
||||
hp.localPosition = new Vector3(0f, 0.6f, 0.9f);
|
||||
holdPoint = hp;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
var kb = Keyboard.current;
|
||||
if (kb == null) return;
|
||||
|
||||
if (_carried == null)
|
||||
_target = FindNearest();
|
||||
|
||||
if (kb.eKey.wasPressedThisFrame)
|
||||
{
|
||||
if (_carried != null) Drop();
|
||||
else if (_target != null) PickUp(_target);
|
||||
}
|
||||
}
|
||||
|
||||
private Carryable FindNearest()
|
||||
{
|
||||
var hits = Physics.OverlapSphere(transform.position, pickupRange);
|
||||
Carryable best = null;
|
||||
float bestDist = float.MaxValue;
|
||||
foreach (var h in hits)
|
||||
{
|
||||
var c = h.GetComponentInParent<Carryable>();
|
||||
if (c == null || c == _carried) continue;
|
||||
float d = Vector3.Distance(transform.position, c.transform.position);
|
||||
if (d < bestDist) { bestDist = d; best = c; }
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
private void PickUp(Carryable c)
|
||||
{
|
||||
_carried = c;
|
||||
_target = null;
|
||||
|
||||
if (c.TryGetComponent<Rigidbody>(out var rb)) rb.isKinematic = true;
|
||||
if (c.TryGetComponent<Collider>(out var col)) col.enabled = false;
|
||||
|
||||
c.transform.SetParent(holdPoint);
|
||||
c.transform.localPosition = Vector3.zero;
|
||||
c.transform.localRotation = Quaternion.identity;
|
||||
}
|
||||
|
||||
private void Drop()
|
||||
{
|
||||
var c = _carried;
|
||||
_carried = null;
|
||||
|
||||
c.transform.SetParent(null);
|
||||
c.transform.position = holdPoint.position;
|
||||
c.transform.rotation = holdPoint.rotation;
|
||||
|
||||
if (c.TryGetComponent<Collider>(out var col)) col.enabled = true;
|
||||
if (c.TryGetComponent<Rigidbody>(out var rb)) rb.isKinematic = false;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
_style ??= new GUIStyle(GUI.skin.box) { fontSize = 18, alignment = TextAnchor.MiddleCenter };
|
||||
|
||||
string msg = null;
|
||||
if (_carried != null) msg = $"E: 내려놓기 — {_carried.Label}";
|
||||
else if (_target != null) msg = $"E: 집기 — {_target.Label}";
|
||||
if (msg == null) return;
|
||||
|
||||
GUI.Label(new Rect(Screen.width / 2f - 150f, 24f, 300f, 32f), msg, _style);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8525e0c0e962c47b78e49a7e3371fe97
|
||||
Reference in New Issue
Block a user