아이템 드랍 및 인벤토리 기능 구현
This commit is contained in:
109
Assets/Scripts/GameBase/DroppedItem.cs
Normal file
109
Assets/Scripts/GameBase/DroppedItem.cs
Normal file
@@ -0,0 +1,109 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public class DroppedItem : NetworkBehaviour, IInteractable
|
||||
{
|
||||
[SerializeField] private MeshFilter myFilter; // 자식의 MeshFilter 연결
|
||||
[SerializeField] private MeshRenderer myRenderer; // 자식의 MeshRenderer 연결
|
||||
[SerializeField] private float dropScale = 0.3f;
|
||||
|
||||
private Rigidbody _rb;
|
||||
[SerializeField] private float jumpForce = 5f; // 위로 솟구치는 힘
|
||||
[SerializeField] private float spreadForce = 2f; // 옆으로 퍼지는 힘
|
||||
[SerializeField] private float rotationSpeed = 100f; // 빙글빙글 도는 속도
|
||||
|
||||
private NetworkVariable<int> _itemDataID = new NetworkVariable<int>();
|
||||
private bool _isProcessed = false; // 중복 획득 방지 (서버용)
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_rb = GetComponent<Rigidbody>();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
UpdateVisuals();
|
||||
_itemDataID.OnValueChanged += (oldVal, newVal) => UpdateVisuals();
|
||||
|
||||
if (IsServer)
|
||||
{
|
||||
ApplyInitialPop();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyInitialPop()
|
||||
{
|
||||
// 위쪽 방향 + 무작위 주변 방향으로 힘을 줍니다.
|
||||
Vector3 force = Vector3.up * jumpForce + Random.insideUnitSphere * spreadForce;
|
||||
_rb.AddForce(force, ForceMode.Impulse);
|
||||
|
||||
// 무작위 회전력(Torque)을 주어 던져지는 느낌을 냅니다.
|
||||
_rb.AddTorque(Random.insideUnitSphere * spreadForce, ForceMode.Impulse);
|
||||
}
|
||||
|
||||
public void Initialize(int id)
|
||||
{
|
||||
if (IsServer) _itemDataID.Value = id;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
// 비주얼만 빙글빙글 돌게 하고 싶다면 (선택 사항)
|
||||
transform.Rotate(Vector3.up * rotationSpeed * Time.deltaTime);
|
||||
}
|
||||
|
||||
private void UpdateVisuals()
|
||||
{
|
||||
ItemData data = ItemDatabase.Instance.GetItemByID(_itemDataID.Value);
|
||||
|
||||
if (data != null && data.originalBlockPrefab != null)
|
||||
{
|
||||
// 원본 프리팹(큰 블록)에서 비주얼 컴포넌트를 찾습니다.
|
||||
MeshFilter sourceFilter = data.originalBlockPrefab.GetComponentInChildren<MeshFilter>();
|
||||
MeshRenderer sourceRenderer = data.originalBlockPrefab.GetComponentInChildren<MeshRenderer>();
|
||||
|
||||
if (sourceFilter != null && myFilter != null)
|
||||
myFilter.sharedMesh = sourceFilter.sharedMesh;
|
||||
|
||||
if (sourceRenderer != null && myRenderer != null)
|
||||
myRenderer.sharedMaterial = sourceRenderer.sharedMaterial;
|
||||
|
||||
// 크기를 작게 줄여서 아이템처럼 보이게 합니다.
|
||||
transform.localScale = Vector3.one * dropScale;
|
||||
}
|
||||
}
|
||||
|
||||
// 플레이어의 OnInteractTap에서 호출될 함수입니다.
|
||||
public void Interact(GameObject interactor)
|
||||
{
|
||||
Debug.Log("<color=yellow>[DroppedItem] 상호작용 시도됨</color>");
|
||||
// 1. 상호작용한 플레이어가 본인(Owner)인지 확인합니다.
|
||||
if (interactor.TryGetComponent<NetworkObject>(out var playerNetObj) && playerNetObj.IsOwner)
|
||||
{
|
||||
// 2. 서버에 이 아이템을 줍겠다고 요청합니다.
|
||||
RequestPickupServerRpc(playerNetObj.NetworkObjectId);
|
||||
Debug.Log("<color=yellow>[DroppedItem] 아이템 획득 요청 서버 RPC 호출됨</color>");
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void RequestPickupServerRpc(ulong playerNetId)
|
||||
{
|
||||
if (_isProcessed) return;
|
||||
|
||||
// 서버에서 아이템 데이터를 가져오고 플레이어 인벤토리에 추가합니다.
|
||||
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(playerNetId, out var playerObj))
|
||||
{
|
||||
if (playerObj.TryGetComponent<PlayerInventory>(out var inventory))
|
||||
{
|
||||
_isProcessed = true;
|
||||
ItemData data = ItemDatabase.Instance.GetItemByID(_itemDataID.Value);
|
||||
|
||||
inventory.AddItem(data); // 인벤토리에 추가
|
||||
|
||||
// 3. 획득 성공 시 네트워크 상에서 제거합니다.
|
||||
GetComponent<NetworkObject>().Despawn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/GameBase/DroppedItem.cs.meta
Normal file
2
Assets/Scripts/GameBase/DroppedItem.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0f19261df3e7c984f89c3427aff0522a
|
||||
12
Assets/Scripts/GameBase/ItemData.cs
Normal file
12
Assets/Scripts/GameBase/ItemData.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(fileName = "New Item", menuName = "Inventory/Item")]
|
||||
public class ItemData : ScriptableObject
|
||||
{
|
||||
public int itemID;
|
||||
public string itemName;
|
||||
public Sprite icon;
|
||||
|
||||
[Header("Visual Source")]
|
||||
public GameObject originalBlockPrefab; // 이제 이것만 있으면 됩니다!
|
||||
}
|
||||
2
Assets/Scripts/GameBase/ItemData.cs.meta
Normal file
2
Assets/Scripts/GameBase/ItemData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a6974a6b8e6365d469ae0566ed23abf5
|
||||
12
Assets/Scripts/GameBase/ItemDatabase.cs
Normal file
12
Assets/Scripts/GameBase/ItemDatabase.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class ItemDatabase : MonoBehaviour
|
||||
{
|
||||
public static ItemDatabase Instance;
|
||||
public List<ItemData> allItems;
|
||||
|
||||
void Awake() => Instance = this;
|
||||
|
||||
public ItemData GetItemByID(int id) => allItems.Find(x => x.itemID == id);
|
||||
}
|
||||
2
Assets/Scripts/GameBase/ItemDatabase.cs.meta
Normal file
2
Assets/Scripts/GameBase/ItemDatabase.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1161d1386968b54483e219677fcc729
|
||||
@@ -1,6 +1,7 @@
|
||||
using NUnit.Framework.Interfaces;
|
||||
using System.Collections;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using System.Collections;
|
||||
|
||||
public class MineableBlock : NetworkBehaviour
|
||||
{
|
||||
@@ -9,6 +10,8 @@ public class MineableBlock : NetworkBehaviour
|
||||
// [동기화] 모든 플레이어가 동일한 블록 체력을 보게 함
|
||||
private NetworkVariable<int> _currentHp = new NetworkVariable<int>();
|
||||
|
||||
[SerializeField] private ItemData dropItemData;
|
||||
[SerializeField] private GameObject genericDropPrefab; // 여기에 위에서 만든 'GenericDroppedItem' 프리팹을 넣으세요.
|
||||
|
||||
[Header("Visuals")]
|
||||
private Outline _outline;
|
||||
@@ -60,6 +63,7 @@ public class MineableBlock : NetworkBehaviour
|
||||
|
||||
private void DestroyBlock()
|
||||
{
|
||||
DropItem();
|
||||
// 2. 서버에서 네트워크 오브젝트 제거 (모든 클라이언트에서 사라짐)
|
||||
GetComponent<NetworkObject>().Despawn();
|
||||
}
|
||||
@@ -84,8 +88,6 @@ public class MineableBlock : NetworkBehaviour
|
||||
private IEnumerator ShakeRoutine()
|
||||
{
|
||||
float elapsed = 0.0f;
|
||||
Debug.Log("흔들림 코루틴 시작"); // 시작 확인
|
||||
|
||||
while (elapsed < shakeDuration)
|
||||
{
|
||||
Vector3 randomOffset = Random.insideUnitSphere * shakeMagnitude;
|
||||
@@ -99,6 +101,22 @@ public class MineableBlock : NetworkBehaviour
|
||||
}
|
||||
|
||||
transform.localPosition = _originalPos;
|
||||
Debug.Log("흔들림 코루틴 종료 및 위치 복구");
|
||||
}
|
||||
|
||||
private void DropItem()
|
||||
{
|
||||
if (!IsServer || dropItemData == null || genericDropPrefab == null) return;
|
||||
|
||||
// 원본 블록이 아니라 '범용 컨테이너'를 소환합니다.
|
||||
GameObject dropObj = Instantiate(genericDropPrefab, transform.position + Vector3.up * 0.5f, Quaternion.identity);
|
||||
|
||||
NetworkObject netObj = dropObj.GetComponent<NetworkObject>();
|
||||
netObj.Spawn();
|
||||
|
||||
// 소환된 컨테이너에 "너는 어떤 아이템의 모양을 따라해야 해"라고 알려줍니다.
|
||||
if (dropObj.TryGetComponent<DroppedItem>(out var droppedItem))
|
||||
{
|
||||
droppedItem.Initialize(dropItemData.itemID);
|
||||
}
|
||||
}
|
||||
}
|
||||
24
Assets/Scripts/Player/ItemPickup.cs
Normal file
24
Assets/Scripts/Player/ItemPickup.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public class ItemPickup : NetworkBehaviour
|
||||
{
|
||||
public ItemData itemData;
|
||||
|
||||
public void SetItem(ItemData data) => itemData = data;
|
||||
|
||||
private void OnTriggerEnter(Collider medical)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
// 플레이어인지 확인
|
||||
if (medical.CompareTag("Player"))
|
||||
{
|
||||
if (medical.TryGetComponent<PlayerInventory>(out var inventory))
|
||||
{
|
||||
inventory.AddItem(itemData);
|
||||
GetComponent<NetworkObject>().Despawn(); // 먹었으므로 제거
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Player/ItemPickup.cs.meta
Normal file
2
Assets/Scripts/Player/ItemPickup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e494b2626c9e354eba277618a850ee8
|
||||
23
Assets/Scripts/Player/PlayerInventory.cs
Normal file
23
Assets/Scripts/Player/PlayerInventory.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
public class PlayerInventory : NetworkBehaviour
|
||||
{
|
||||
// 아이템 이름과 개수를 저장
|
||||
private Dictionary<string, int> items = new Dictionary<string, int>();
|
||||
|
||||
public void AddItem(ItemData data)
|
||||
{
|
||||
if (!IsServer) return;
|
||||
|
||||
if (items.ContainsKey(data.itemName))
|
||||
items[data.itemName]++;
|
||||
else
|
||||
items.Add(data.itemName, 1);
|
||||
|
||||
Debug.Log($"[Inventory] {data.itemName} 획득! (현재: {items[data.itemName]}개)");
|
||||
|
||||
// 여기서 클라이언트 UI를 갱신하는 ClientRpc를 호출할 수 있습니다.
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Player/PlayerInventory.cs.meta
Normal file
2
Assets/Scripts/Player/PlayerInventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a1edd64a97d0b4f4aa71f0a23f32564f
|
||||
Reference in New Issue
Block a user