장비 개념 추가 및 Kaykit 애셋 추가
This commit is contained in:
@@ -11,4 +11,13 @@ public class ItemData : ScriptableObject
|
||||
|
||||
[Header("Visual Source")]
|
||||
public GameObject originalBlockPrefab; // 이제 이것만 있으면 됩니다!
|
||||
|
||||
[Header("Tool Settings")]
|
||||
public bool isTool; // 도구 여부
|
||||
public PlayerActionData toolAction; // 이 도구를 들었을 때 나갈 액션 (예: MiningActionData)
|
||||
|
||||
[Header("Visual Settings")]
|
||||
public GameObject toolPrefab; // 캐릭터 손에 스폰될 3D 프리팹
|
||||
public Vector3 equipPositionOffset; // 손 위치 미세 조정
|
||||
public Vector3 equipRotationOffset; // 손 회전 미세 조정
|
||||
}
|
||||
58
Assets/Scripts/Player/PlayerEquipmentHandler.cs
Normal file
58
Assets/Scripts/Player/PlayerEquipmentHandler.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
public class PlayerEquipmentHandler : NetworkBehaviour
|
||||
{
|
||||
[SerializeField] private Transform toolAnchor; // 캐릭터 손의 소켓 위치
|
||||
private PlayerInventory _inventory;
|
||||
private GameObject _currentToolInstance; // 현재 생성된 도구 모델
|
||||
|
||||
void Awake()
|
||||
{
|
||||
_inventory = GetComponent<PlayerInventory>();
|
||||
}
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
// 인벤토리의 슬롯 변경 이벤트 구독
|
||||
// OnSlotChanged는 (이전 값, 새 값) 두 개의 인자를 전달합니다.
|
||||
_inventory.OnSlotChanged += HandleSlotChanged;
|
||||
|
||||
// 게임 시작 시 처음에 들고 있는 아이템 모델 생성
|
||||
UpdateEquippedModel(_inventory.SelectedSlotIndex);
|
||||
}
|
||||
|
||||
private void HandleSlotChanged(int previousValue, int newValue)
|
||||
{
|
||||
UpdateEquippedModel(newValue);
|
||||
}
|
||||
|
||||
private void UpdateEquippedModel(int slotIndex)
|
||||
{
|
||||
// 1. 기존 도구가 있다면 파괴
|
||||
if (_currentToolInstance != null)
|
||||
{
|
||||
Destroy(_currentToolInstance);
|
||||
}
|
||||
|
||||
// 2. 현재 선택된 슬롯의 데이터 확인
|
||||
ItemData data = _inventory.GetItemDataInSlot(slotIndex);
|
||||
|
||||
// 3. 도구인 경우에만 모델 생성
|
||||
if (data != null && data.isTool && data.toolPrefab != null)
|
||||
{
|
||||
_currentToolInstance = Instantiate(data.toolPrefab, toolAnchor);
|
||||
|
||||
// ItemData에 설정된 오프셋 적용
|
||||
_currentToolInstance.transform.localPosition = data.equipPositionOffset;
|
||||
_currentToolInstance.transform.localRotation = Quaternion.Euler(data.equipRotationOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
// 이벤트 구독 해제 (메모리 누수 방지)
|
||||
if (_inventory != null)
|
||||
_inventory.OnSlotChanged -= HandleSlotChanged;
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Player/PlayerEquipmentHandler.cs.meta
Normal file
2
Assets/Scripts/Player/PlayerEquipmentHandler.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a865519dd705e524bbbc9a1cf5ecd72a
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
public class PlayerInventory : NetworkBehaviour
|
||||
{
|
||||
@@ -9,6 +10,30 @@ public class PlayerInventory : NetworkBehaviour
|
||||
public int slotCount = 5;
|
||||
public float maxWeight = 50f;
|
||||
|
||||
[System.Serializable]
|
||||
public struct DefaultItem
|
||||
{
|
||||
public ItemData item; // 인스펙터에서 아이템 에셋 드래그
|
||||
public int amount; // 초기 개수
|
||||
}
|
||||
|
||||
[Header("Starting Loadout")]
|
||||
[SerializeField] private List<DefaultItem> startingItems; // 기본 지급 아이템 리스트
|
||||
|
||||
// 1. 실제 데이터 저장소 (Private)
|
||||
private NetworkVariable<int> _selectedSlotIndex = new NetworkVariable<int>(0,
|
||||
NetworkVariableReadPermission.Everyone, NetworkVariableWritePermission.Server);
|
||||
|
||||
// 2. 외부에서 읽기 전용으로 값에 접근 (기존 유지)
|
||||
public int SelectedSlotIndex => _selectedSlotIndex.Value;
|
||||
|
||||
// 3. [핵심] 외부에서 이벤트에 함수를 등록(+=)할 수 있도록 델리게이트 노출
|
||||
public NetworkVariable<int>.OnValueChangedDelegate OnSlotChanged
|
||||
{
|
||||
get => _selectedSlotIndex.OnValueChanged;
|
||||
set => _selectedSlotIndex.OnValueChanged = value;
|
||||
}
|
||||
|
||||
public struct InventorySlot : INetworkSerializable, IEquatable<InventorySlot>
|
||||
{
|
||||
public int ItemID; // string에서 int로 변경
|
||||
@@ -39,12 +64,9 @@ public class PlayerInventory : NetworkBehaviour
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
// 서버에서만 슬롯 초기화
|
||||
if (IsServer && Slots.Count == 0)
|
||||
if (IsServer)
|
||||
{
|
||||
// 설정된 slotCount만큼 데이터 슬롯 생성
|
||||
for (int i = 0; i < slotCount; i++)
|
||||
Slots.Add(new InventorySlot { ItemID = -1, Amount = 0 });
|
||||
InitializeInventory();
|
||||
}
|
||||
|
||||
// 로컬 플레이어라면 UI 연결
|
||||
@@ -86,4 +108,64 @@ public class PlayerInventory : NetworkBehaviour
|
||||
_currentWeight.Value += addedWeight;
|
||||
}
|
||||
}
|
||||
|
||||
// 슬롯 선택 요청 (최신 RPC 방식 적용)
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void ChangeSelectedSlotRpc(int index)
|
||||
{
|
||||
if (index >= 0 && index < slotCount)
|
||||
{
|
||||
Debug.Log($"<color=green>[PlayerInventory] 슬롯 선택 변경 요청: {index}</color>");
|
||||
_selectedSlotIndex.Value = index;
|
||||
}
|
||||
}
|
||||
|
||||
// 현재 선택된 슬롯의 아이템 데이터를 가져오는 편의 기능
|
||||
public ItemData GetSelectedItemData()
|
||||
{
|
||||
var slot = Slots[SelectedSlotIndex];
|
||||
if (slot.ItemID == -1) return null;
|
||||
return ItemDatabase.Instance.GetItemByID(slot.ItemID);
|
||||
}
|
||||
|
||||
private void InitializeInventory()
|
||||
{
|
||||
// 슬롯을 깨끗하게 비우고 시작 (이미 데이터가 있을 경우를 대비)
|
||||
if (Slots.Count > 0) Slots.Clear();
|
||||
_currentWeight.Value = 0;
|
||||
|
||||
// 전체 슬롯 개수만큼 빈 슬롯(-1) 먼저 생성
|
||||
for (int i = 0; i < slotCount; i++)
|
||||
{
|
||||
Slots.Add(new InventorySlot { ItemID = -1, Amount = 0 });
|
||||
}
|
||||
|
||||
// 설정된 기본 아이템들을 순서대로 채워 넣음
|
||||
for (int i = 0; i < startingItems.Count && i < slotCount; i++)
|
||||
{
|
||||
if (startingItems[i].item != null)
|
||||
{
|
||||
ItemData data = startingItems[i].item;
|
||||
int amount = startingItems[i].amount;
|
||||
|
||||
Slots[i] = new InventorySlot
|
||||
{
|
||||
ItemID = data.itemID,
|
||||
Amount = amount
|
||||
};
|
||||
|
||||
// 초기 무게 합산
|
||||
_currentWeight.Value += data.weight * amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PlayerInventory.cs에 추가
|
||||
public ItemData GetItemDataInSlot(int index)
|
||||
{
|
||||
if (index < 0 || index >= Slots.Count) return null;
|
||||
var slot = Slots[index];
|
||||
if (slot.ItemID == -1) return null;
|
||||
return ItemDatabase.Instance.GetItemByID(slot.ItemID);
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,9 @@ public class PlayerNetworkController : NetworkBehaviour
|
||||
[SerializeField] private float visionRadius = 5f; // 시야 반경
|
||||
private float _lastRevealTime;
|
||||
|
||||
[Header("Action System")]
|
||||
[SerializeField] private PlayerActionData miningAction; // 채광에 대한 시간/애니메이션/효과 데이터
|
||||
private PlayerActionHandler _actionHandler; // 새 핸들러 연결[Header("Action Data")]
|
||||
[Header("Inventory & Action")]
|
||||
private PlayerInventory _inventory; // 인벤토리 참조 추가
|
||||
private PlayerActionHandler _actionHandler;
|
||||
|
||||
private RectTransform _crosshairRect;
|
||||
private MineableBlock _currentTargetBlock; // 현재 강조 중인 블록 저장
|
||||
@@ -105,6 +105,13 @@ public class PlayerNetworkController : NetworkBehaviour
|
||||
_inputActions.Player.Interact.started += ctx => _isHoldingInteract = true;
|
||||
_inputActions.Player.Interact.canceled += ctx => _isHoldingInteract = false;
|
||||
|
||||
_inputActions.Player.Select1.performed += ctx => _inventory.ChangeSelectedSlotRpc(0);
|
||||
_inputActions.Player.Select2.performed += ctx => _inventory.ChangeSelectedSlotRpc(1);
|
||||
_inputActions.Player.Select3.performed += ctx => _inventory.ChangeSelectedSlotRpc(2);
|
||||
_inputActions.Player.Select4.performed += ctx => _inventory.ChangeSelectedSlotRpc(3);
|
||||
_inputActions.Player.Select5.performed += ctx => _inventory.ChangeSelectedSlotRpc(4);
|
||||
_inputActions.Player.Select6.performed += ctx => _inventory.ChangeSelectedSlotRpc(5);
|
||||
|
||||
_inputActions.Enable();
|
||||
}
|
||||
|
||||
@@ -113,7 +120,12 @@ public class PlayerNetworkController : NetworkBehaviour
|
||||
_controller = GetComponent<CharacterController>();
|
||||
_animator = GetComponent<Animator>();
|
||||
_traveler = GetComponent<TunnelTraveler>();
|
||||
|
||||
// --- 참조 초기화 추가 ---
|
||||
_inventory = GetComponent<PlayerInventory>();
|
||||
_actionHandler = GetComponent<PlayerActionHandler>();
|
||||
|
||||
if (_inventory == null) Debug.LogError("PlayerInventory 컴포넌트를 찾을 수 없습니다!");
|
||||
}
|
||||
|
||||
void Update()
|
||||
@@ -175,13 +187,31 @@ public class PlayerNetworkController : NetworkBehaviour
|
||||
}
|
||||
|
||||
// 1. 액션 (좌클릭) - 대상이 없어도 나감
|
||||
// PlayerNetworkController.cs 중 일부
|
||||
private void OnActionInput()
|
||||
{
|
||||
if (!IsOwner || _actionHandler.IsBusy) return;
|
||||
|
||||
// 조준 중인 블록이 있으면 넘기고, 없으면 null을 넘겨서 실행
|
||||
GameObject target = _lastHighlightedBlock?.gameObject;
|
||||
_actionHandler.PerformAction(miningAction, target);
|
||||
ItemData selectedItem = _inventory.GetSelectedItemData();
|
||||
|
||||
// 로그 1: 아이템 확인
|
||||
if (selectedItem == null) { Debug.Log("선택된 아이템이 없음"); return; }
|
||||
|
||||
// 로그 2: 도구 여부 및 액션 데이터 확인
|
||||
Debug.Log($"현재 아이템: {selectedItem.itemName}, 도구여부: {selectedItem.isTool}, 액션데이터: {selectedItem.toolAction != null}");
|
||||
|
||||
if (selectedItem.isTool && selectedItem.toolAction != null)
|
||||
{
|
||||
if (_lastHighlightedBlock != null)
|
||||
{
|
||||
Debug.Log($"채광 시작: {_lastHighlightedBlock.name}");
|
||||
_actionHandler.PerformAction(selectedItem.toolAction, _lastHighlightedBlock.gameObject);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Log("조준된 블록이 없음 (하이라이트 확인 필요)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 인터랙션 (F키) - 대상이 없으면 아예 시작 안 함
|
||||
|
||||
@@ -46,6 +46,9 @@ public class QuickslotUI : MonoBehaviour
|
||||
{
|
||||
var data = _linkedInventory.Slots[i];
|
||||
_uiSlots[i].UpdateView(data.ItemID, data.Amount);
|
||||
|
||||
// 선택된 슬롯만 하이라이트 활성화
|
||||
_uiSlots[i].SetSelection(i == _linkedInventory.SelectedSlotIndex);
|
||||
}
|
||||
UpdateWeightDisplay();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user