자원 채집 및 반환 기능
텍스트 기능 및 폰트 추가
This commit is contained in:
@@ -69,7 +69,7 @@ namespace Northbound
|
||||
Debug.Log($"플레이어 {playerId} 공격! (적중: {hits.Length}개)");
|
||||
}
|
||||
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void AttackServerRpc(ulong playerId, ulong targetNetworkId)
|
||||
{
|
||||
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(targetNetworkId, out NetworkObject targetObj))
|
||||
|
||||
@@ -439,7 +439,7 @@ namespace Northbound
|
||||
}
|
||||
|
||||
|
||||
private void OnDestroy()
|
||||
override public void OnDestroy()
|
||||
{
|
||||
DestroyPreview();
|
||||
|
||||
@@ -447,6 +447,8 @@ namespace Northbound
|
||||
{
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
223
Assets/Scripts/Core.cs
Normal file
223
Assets/Scripts/Core.cs
Normal file
@@ -0,0 +1,223 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어가 자원을 건내받아 게임의 전역 자원으로 관리하는 중앙 허브
|
||||
/// </summary>
|
||||
public class Core : NetworkBehaviour, IInteractable
|
||||
{
|
||||
[Header("Core Settings")]
|
||||
public int maxStorageCapacity = 1000; // 코어의 최대 저장 용량
|
||||
public bool unlimitedStorage = false; // 무제한 저장소
|
||||
|
||||
[Header("Deposit Settings")]
|
||||
public bool depositAll = true; // true: 전부 건네기, false: 일부만 건네기
|
||||
public int depositAmountPerInteraction = 10; // depositAll이 false일 때 한 번에 건네는 양
|
||||
|
||||
[Header("Animation")]
|
||||
public string interactionAnimationTrigger = "Deposit"; // 플레이어 애니메이션 트리거
|
||||
|
||||
[Header("Equipment")]
|
||||
public InteractionEquipmentData equipmentData = null; // 도구 필요 없음
|
||||
|
||||
[Header("Visual")]
|
||||
public GameObject depositEffectPrefab;
|
||||
public Transform effectSpawnPoint;
|
||||
|
||||
private NetworkVariable<int> _totalResources = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
public int TotalResources => _totalResources.Value;
|
||||
public int MaxStorageCapacity => maxStorageCapacity;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
_totalResources.Value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
// 저장소가 가득 찼는지 확인 (무제한이 아닐 때)
|
||||
if (!unlimitedStorage && _totalResources.Value >= maxStorageCapacity)
|
||||
return false;
|
||||
|
||||
// 플레이어가 자원을 가지고 있는지 확인
|
||||
if (NetworkManager.Singleton != null &&
|
||||
NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerResourceInventory>();
|
||||
if (playerInventory != null)
|
||||
{
|
||||
// 플레이어가 자원을 가지고 있어야 함
|
||||
return playerInventory.CurrentResourceAmount > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
DepositResourceServerRpc(playerId);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void DepositResourceServerRpc(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
// 플레이어 인벤토리 가져오기
|
||||
var playerObject = NetworkManager.Singleton.ConnectedClients[playerId].PlayerObject;
|
||||
if (playerObject == null)
|
||||
return;
|
||||
|
||||
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
|
||||
if (playerInventory == null)
|
||||
{
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerResourceInventory 컴포넌트가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
int playerResourceAmount = playerInventory.CurrentResourceAmount;
|
||||
if (playerResourceAmount <= 0)
|
||||
{
|
||||
Debug.Log($"플레이어 {playerId}가 건낼 자원이 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
int depositAmount;
|
||||
|
||||
if (depositAll)
|
||||
{
|
||||
// 전부 건네기
|
||||
depositAmount = playerResourceAmount;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 일부만 건네기
|
||||
depositAmount = Mathf.Min(depositAmountPerInteraction, playerResourceAmount);
|
||||
}
|
||||
|
||||
// 무제한 저장소가 아니면 용량 제한 확인
|
||||
if (!unlimitedStorage)
|
||||
{
|
||||
int availableSpace = maxStorageCapacity - _totalResources.Value;
|
||||
depositAmount = Mathf.Min(depositAmount, availableSpace);
|
||||
}
|
||||
|
||||
if (depositAmount <= 0)
|
||||
{
|
||||
Debug.Log($"코어의 저장 공간이 부족합니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 플레이어로부터 자원 차감
|
||||
playerInventory.RemoveResourceServerRpc(depositAmount);
|
||||
|
||||
// 코어에 자원 추가
|
||||
_totalResources.Value += depositAmount;
|
||||
|
||||
Debug.Log($"플레이어 {playerId}가 {depositAmount} 자원을 코어에 건넸습니다. 코어 총 자원: {_totalResources.Value}" +
|
||||
(unlimitedStorage ? "" : $"/{maxStorageCapacity}"));
|
||||
|
||||
ShowDepositEffectClientRpc();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.ClientsAndHost)]
|
||||
private void ShowDepositEffectClientRpc()
|
||||
{
|
||||
if (depositEffectPrefab != null && effectSpawnPoint != null)
|
||||
{
|
||||
GameObject effect = Instantiate(depositEffectPrefab, effectSpawnPoint.position, effectSpawnPoint.rotation);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 게임 시스템이 코어의 자원을 사용 (건물 건설 등)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void ConsumeResourceServerRpc(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, _totalResources.Value);
|
||||
_totalResources.Value -= actualAmount;
|
||||
|
||||
Debug.Log($"코어에서 {actualAmount} 자원을 사용했습니다. 남은 자원: {_totalResources.Value}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원을 사용할 수 있는지 확인
|
||||
/// </summary>
|
||||
public bool CanConsumeResource(int amount)
|
||||
{
|
||||
return _totalResources.Value >= amount;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 코어에 자원 추가 (디버그/관리자 기능)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void AddResourceServerRpc(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
if (!unlimitedStorage)
|
||||
{
|
||||
int availableSpace = maxStorageCapacity - _totalResources.Value;
|
||||
amount = Mathf.Min(amount, availableSpace);
|
||||
}
|
||||
|
||||
_totalResources.Value += amount;
|
||||
Debug.Log($"코어에 {amount} 자원이 추가되었습니다. 현재: {_totalResources.Value}");
|
||||
}
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
if (unlimitedStorage)
|
||||
{
|
||||
return depositAll ?
|
||||
$"[E] 자원 모두 건네기" :
|
||||
$"[E] 자원 건네기 ({depositAmountPerInteraction}개씩)";
|
||||
}
|
||||
|
||||
if (_totalResources.Value >= maxStorageCapacity)
|
||||
return "코어 저장소 가득 찼음";
|
||||
|
||||
return depositAll ?
|
||||
$"[E] 자원 모두 건네기 ({_totalResources.Value}/{maxStorageCapacity})" :
|
||||
$"[E] 자원 건네기 ({_totalResources.Value}/{maxStorageCapacity})";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
return interactionAnimationTrigger;
|
||||
}
|
||||
|
||||
public InteractionEquipmentData GetEquipmentData()
|
||||
{
|
||||
return equipmentData;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Core.cs.meta
Normal file
2
Assets/Scripts/Core.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c94274e2af2c8d4f827fe52b26c4410
|
||||
67
Assets/Scripts/CoreResourceManager.cs
Normal file
67
Assets/Scripts/CoreResourceManager.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// Core의 자원을 관리하고 다른 시스템에서 접근할 수 있도록 하는 매니저
|
||||
/// </summary>
|
||||
public class CoreResourceManager : MonoBehaviour
|
||||
{
|
||||
private static CoreResourceManager _instance;
|
||||
public static CoreResourceManager Instance => _instance;
|
||||
|
||||
[Header("References")]
|
||||
public Core mainCore; // 메인 코어 참조
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (_instance != null && _instance != this)
|
||||
{
|
||||
Destroy(gameObject);
|
||||
return;
|
||||
}
|
||||
|
||||
_instance = this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원을 사용할 수 있는지 확인
|
||||
/// </summary>
|
||||
public bool CanAfford(int cost)
|
||||
{
|
||||
if (mainCore == null)
|
||||
{
|
||||
Debug.LogWarning("메인 코어가 설정되지 않았습니다.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return mainCore.CanConsumeResource(cost);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원을 소비
|
||||
/// </summary>
|
||||
public void SpendResources(int amount)
|
||||
{
|
||||
if (mainCore == null)
|
||||
{
|
||||
Debug.LogWarning("메인 코어가 설정되지 않았습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
mainCore.ConsumeResourceServerRpc(amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 현재 자원량 가져오기
|
||||
/// </summary>
|
||||
public int GetCurrentResources()
|
||||
{
|
||||
if (mainCore == null)
|
||||
return 0;
|
||||
|
||||
return mainCore.TotalResources;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/CoreResourceManager.cs.meta
Normal file
2
Assets/Scripts/CoreResourceManager.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ed03291507256a47913eae4dee429c7
|
||||
212
Assets/Scripts/GameResourceUI.cs
Normal file
212
Assets/Scripts/GameResourceUI.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
using UnityEngine;
|
||||
using TMPro;
|
||||
using Unity.Netcode;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 코어와 플레이어의 자원을 화면에 표시하는 통합 UI
|
||||
/// </summary>
|
||||
public class GameResourceUI : MonoBehaviour
|
||||
{
|
||||
[Header("Core Resource")]
|
||||
public TextMeshProUGUI coreResourceText;
|
||||
public TMP_FontAsset coreFontAsset; // 코어 텍스트용 폰트
|
||||
|
||||
[Header("Player Resource")]
|
||||
public TextMeshProUGUI playerResourceText;
|
||||
public TMP_FontAsset playerFontAsset; // 플레이어 텍스트용 폰트
|
||||
public bool showPlayerResource = true;
|
||||
|
||||
[Header("Display Settings")]
|
||||
public string corePrefix = "코어: ";
|
||||
public string playerPrefix = "보유: ";
|
||||
public string separator = " / ";
|
||||
|
||||
[Header("Color Settings")]
|
||||
public bool useColorCoding = true;
|
||||
public Color normalColor = Color.white;
|
||||
public Color fullColor = Color.yellow;
|
||||
public Color emptyColor = Color.gray;
|
||||
|
||||
[Header("Update Settings")]
|
||||
public float updateInterval = 0.1f;
|
||||
|
||||
private float _lastUpdateTime;
|
||||
private int _cachedCoreResource = -1;
|
||||
private int _cachedPlayerResource = -1;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// 폰트 애셋 적용
|
||||
ApplyFontAssets();
|
||||
}
|
||||
|
||||
private void ApplyFontAssets()
|
||||
{
|
||||
if (coreResourceText != null && coreFontAsset != null)
|
||||
{
|
||||
coreResourceText.font = coreFontAsset;
|
||||
}
|
||||
|
||||
if (playerResourceText != null && playerFontAsset != null)
|
||||
{
|
||||
playerResourceText.font = playerFontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Time.time - _lastUpdateTime >= updateInterval)
|
||||
{
|
||||
UpdateUI();
|
||||
_lastUpdateTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateUI()
|
||||
{
|
||||
UpdateCoreResourceDisplay();
|
||||
|
||||
if (showPlayerResource)
|
||||
{
|
||||
UpdatePlayerResourceDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateCoreResourceDisplay()
|
||||
{
|
||||
if (coreResourceText == null)
|
||||
return;
|
||||
|
||||
if (CoreResourceManager.Instance == null || CoreResourceManager.Instance.mainCore == null)
|
||||
{
|
||||
coreResourceText.text = corePrefix + "---";
|
||||
if (useColorCoding)
|
||||
coreResourceText.color = emptyColor;
|
||||
return;
|
||||
}
|
||||
|
||||
int currentResources = CoreResourceManager.Instance.GetCurrentResources();
|
||||
|
||||
if (currentResources != _cachedCoreResource)
|
||||
{
|
||||
_cachedCoreResource = currentResources;
|
||||
|
||||
var core = CoreResourceManager.Instance.mainCore;
|
||||
|
||||
if (core.unlimitedStorage)
|
||||
{
|
||||
coreResourceText.text = $"{corePrefix}{currentResources}";
|
||||
}
|
||||
else
|
||||
{
|
||||
coreResourceText.text = $"{corePrefix}{currentResources}{separator}{core.MaxStorageCapacity}";
|
||||
|
||||
// 색상 코딩
|
||||
if (useColorCoding)
|
||||
{
|
||||
float fillPercentage = (float)currentResources / core.MaxStorageCapacity;
|
||||
|
||||
if (fillPercentage >= 1.0f)
|
||||
coreResourceText.color = fullColor;
|
||||
else if (fillPercentage <= 0.1f)
|
||||
coreResourceText.color = emptyColor;
|
||||
else
|
||||
coreResourceText.color = normalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdatePlayerResourceDisplay()
|
||||
{
|
||||
if (playerResourceText == null)
|
||||
return;
|
||||
|
||||
var localPlayer = GetLocalPlayer();
|
||||
if (localPlayer == null)
|
||||
{
|
||||
playerResourceText.text = playerPrefix + "---";
|
||||
if (useColorCoding)
|
||||
playerResourceText.color = emptyColor;
|
||||
return;
|
||||
}
|
||||
|
||||
var inventory = localPlayer.GetComponent<PlayerResourceInventory>();
|
||||
if (inventory == null)
|
||||
{
|
||||
playerResourceText.text = playerPrefix + "---";
|
||||
if (useColorCoding)
|
||||
playerResourceText.color = emptyColor;
|
||||
return;
|
||||
}
|
||||
|
||||
int currentAmount = inventory.CurrentResourceAmount;
|
||||
|
||||
if (currentAmount != _cachedPlayerResource)
|
||||
{
|
||||
_cachedPlayerResource = currentAmount;
|
||||
playerResourceText.text = $"{playerPrefix}{currentAmount}{separator}{inventory.MaxResourceCapacity}";
|
||||
|
||||
// 색상 코딩
|
||||
if (useColorCoding)
|
||||
{
|
||||
float fillPercentage = (float)currentAmount / inventory.MaxResourceCapacity;
|
||||
|
||||
if (fillPercentage >= 1.0f)
|
||||
playerResourceText.color = fullColor;
|
||||
else if (fillPercentage <= 0.1f)
|
||||
playerResourceText.color = emptyColor;
|
||||
else
|
||||
playerResourceText.color = normalColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private GameObject GetLocalPlayer()
|
||||
{
|
||||
if (NetworkManager.Singleton == null || !NetworkManager.Singleton.IsClient)
|
||||
return null;
|
||||
|
||||
ulong localClientId = NetworkManager.Singleton.LocalClientId;
|
||||
|
||||
if (NetworkManager.Singleton.ConnectedClients.TryGetValue(localClientId, out var client))
|
||||
{
|
||||
return client.PlayerObject?.gameObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
ApplyFontAssets();
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 런타임에서 폰트를 변경할 때 사용
|
||||
/// </summary>
|
||||
public void SetCoreFontAsset(TMP_FontAsset fontAsset)
|
||||
{
|
||||
coreFontAsset = fontAsset;
|
||||
if (coreResourceText != null)
|
||||
{
|
||||
coreResourceText.font = fontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 런타임에서 폰트를 변경할 때 사용
|
||||
/// </summary>
|
||||
public void SetPlayerFontAsset(TMP_FontAsset fontAsset)
|
||||
{
|
||||
playerFontAsset = fontAsset;
|
||||
if (playerResourceText != null)
|
||||
{
|
||||
playerResourceText.font = fontAsset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/GameResourceUI.cs.meta
Normal file
2
Assets/Scripts/GameResourceUI.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e401fa73ad837e84dbe92d6f58d1ee3d
|
||||
@@ -1,141 +0,0 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 상호작용 대상 - 광산 (채광하기)
|
||||
/// </summary>
|
||||
public class Mine : NetworkBehaviour, IInteractable
|
||||
{
|
||||
[Header("Mine Settings")]
|
||||
public bool infiniteResources = false; // 무제한 자원
|
||||
public int maxResources = 100;
|
||||
public int resourcesPerMining = 10;
|
||||
public float miningCooldown = 2f;
|
||||
public string resourceName = "광석";
|
||||
|
||||
[Header("Animation")]
|
||||
public string interactionAnimationTrigger = "Mining"; // 플레이어 애니메이션 트리거
|
||||
|
||||
[Header("Equipment")]
|
||||
public InteractionEquipmentData equipmentData = new InteractionEquipmentData
|
||||
{
|
||||
socketName = "RightHand",
|
||||
attachOnStart = true,
|
||||
detachOnEnd = true
|
||||
};
|
||||
|
||||
[Header("Visual")]
|
||||
public GameObject miningEffectPrefab;
|
||||
public Transform effectSpawnPoint;
|
||||
|
||||
private NetworkVariable<int> _currentResources = new NetworkVariable<int>(
|
||||
100,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private float _lastMiningTime;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
_currentResources.Value = maxResources;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
// 무제한 자원이면 항상 채굴 가능
|
||||
if (infiniteResources)
|
||||
return Time.time - _lastMiningTime >= miningCooldown;
|
||||
|
||||
if (_currentResources.Value <= 0)
|
||||
return false;
|
||||
|
||||
return Time.time - _lastMiningTime >= miningCooldown;
|
||||
}
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
MineResourceServerRpc(playerId);
|
||||
}
|
||||
|
||||
[ServerRpc(RequireOwnership = false)]
|
||||
private void MineResourceServerRpc(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
int minedAmount = resourcesPerMining;
|
||||
|
||||
// 무제한이 아니면 자원 감소
|
||||
if (!infiniteResources)
|
||||
{
|
||||
minedAmount = Mathf.Min(resourcesPerMining, _currentResources.Value);
|
||||
_currentResources.Value -= minedAmount;
|
||||
}
|
||||
|
||||
_lastMiningTime = Time.time;
|
||||
|
||||
Debug.Log($"플레이어 {playerId}가 {minedAmount} {resourceName}을(를) 채굴했습니다. " +
|
||||
(infiniteResources ? "(무제한)" : $"남은 자원: {_currentResources.Value}"));
|
||||
|
||||
ShowMiningEffectClientRpc();
|
||||
|
||||
if (!infiniteResources && _currentResources.Value <= 0)
|
||||
{
|
||||
OnResourcesDepleted();
|
||||
}
|
||||
}
|
||||
|
||||
[ClientRpc]
|
||||
private void ShowMiningEffectClientRpc()
|
||||
{
|
||||
if (miningEffectPrefab != null && effectSpawnPoint != null)
|
||||
{
|
||||
GameObject effect = Instantiate(miningEffectPrefab, effectSpawnPoint.position, effectSpawnPoint.rotation);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnResourcesDepleted()
|
||||
{
|
||||
Debug.Log("광산이 고갈되었습니다!");
|
||||
}
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
if (infiniteResources)
|
||||
{
|
||||
return $"[E] {resourceName} 채굴 (무제한)";
|
||||
}
|
||||
|
||||
if (_currentResources.Value > 0)
|
||||
{
|
||||
return $"[E] {resourceName} 채굴 ({_currentResources.Value}/{maxResources})";
|
||||
}
|
||||
return "고갈된 광산";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
return interactionAnimationTrigger;
|
||||
}
|
||||
|
||||
public InteractionEquipmentData GetEquipmentData()
|
||||
{
|
||||
return equipmentData;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e31364bc942ad7e41be627b7a00b206e
|
||||
@@ -121,12 +121,14 @@ namespace Northbound
|
||||
return spawnPoints[spawnIndex].rotation;
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
override public void OnDestroy()
|
||||
{
|
||||
if (NetworkManager.Singleton != null)
|
||||
{
|
||||
NetworkManager.Singleton.OnClientConnectedCallback -= OnClientConnected;
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -80,12 +80,14 @@ namespace Northbound
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
override public void OnDestroy()
|
||||
{
|
||||
if (_inputActions != null)
|
||||
{
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -235,12 +235,14 @@ namespace Northbound
|
||||
GUI.Label(new Rect(Screen.width / 2 - 200, Screen.height - 100, 400, 50), prompt, style);
|
||||
}
|
||||
|
||||
private void OnDestroy()
|
||||
override public void OnDestroy()
|
||||
{
|
||||
if (_inputActions != null)
|
||||
{
|
||||
_inputActions.Dispose();
|
||||
}
|
||||
|
||||
base.OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Assets/Scripts/PlayerResourceInventory.cs
Normal file
76
Assets/Scripts/PlayerResourceInventory.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 플레이어의 자원 인벤토리 관리
|
||||
/// </summary>
|
||||
public class PlayerResourceInventory : NetworkBehaviour
|
||||
{
|
||||
[Header("Inventory Settings")]
|
||||
public int maxResourceCapacity = 100; // 최대 자원 보유량
|
||||
|
||||
private NetworkVariable<int> _currentResourceAmount = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
public int CurrentResourceAmount => _currentResourceAmount.Value;
|
||||
public int MaxResourceCapacity => maxResourceCapacity;
|
||||
|
||||
/// <summary>
|
||||
/// 자원을 추가할 수 있는지 확인
|
||||
/// </summary>
|
||||
public bool CanAddResource(int amount)
|
||||
{
|
||||
return _currentResourceAmount.Value + amount <= maxResourceCapacity;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 추가 가능한 최대 자원량 계산
|
||||
/// </summary>
|
||||
public int GetAvailableSpace()
|
||||
{
|
||||
return maxResourceCapacity - _currentResourceAmount.Value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원 추가 (서버에서만 호출)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void AddResourceServerRpc(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, maxResourceCapacity - _currentResourceAmount.Value);
|
||||
_currentResourceAmount.Value += actualAmount;
|
||||
|
||||
Debug.Log($"플레이어 {OwnerClientId} - 자원 추가: +{actualAmount}, 현재: {_currentResourceAmount.Value}/{maxResourceCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원 제거 (서버에서만 호출)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void RemoveResourceServerRpc(int amount)
|
||||
{
|
||||
if (amount <= 0) return;
|
||||
|
||||
int actualAmount = Mathf.Min(amount, _currentResourceAmount.Value);
|
||||
_currentResourceAmount.Value -= actualAmount;
|
||||
|
||||
Debug.Log($"플레이어 {OwnerClientId} - 자원 사용: -{actualAmount}, 현재: {_currentResourceAmount.Value}/{maxResourceCapacity}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 자원 설정 (서버에서만 호출)
|
||||
/// </summary>
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
public void SetResourceServerRpc(int amount)
|
||||
{
|
||||
_currentResourceAmount.Value = Mathf.Clamp(amount, 0, maxResourceCapacity);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/PlayerResourceInventory.cs.meta
Normal file
2
Assets/Scripts/PlayerResourceInventory.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3c64072402b0a3f46a674eb73c5541ac
|
||||
190
Assets/Scripts/Resource.cs
Normal file
190
Assets/Scripts/Resource.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 상호작용 대상 - 자원 채집
|
||||
/// </summary>
|
||||
public class Resource : NetworkBehaviour, IInteractable
|
||||
{
|
||||
[Header("Resource Settings")]
|
||||
public int maxResources = 100;
|
||||
public int resourcesPerGathering = 10;
|
||||
public float gatheringCooldown = 2f;
|
||||
public string resourceName = "광석";
|
||||
|
||||
[Header("Resource Recharge")]
|
||||
public float rechargeInterval = 5f; // 충전 주기 (초)
|
||||
public int rechargeAmount = 10; // 주기당 충전량
|
||||
|
||||
[Header("Animation")]
|
||||
public string interactionAnimationTrigger = "Mining"; // 플레이어 애니메이션 트리거
|
||||
|
||||
[Header("Equipment")]
|
||||
public InteractionEquipmentData equipmentData = new InteractionEquipmentData
|
||||
{
|
||||
socketName = "RightHand",
|
||||
attachOnStart = true,
|
||||
detachOnEnd = true
|
||||
};
|
||||
|
||||
[Header("Visual")]
|
||||
public GameObject gatheringEffectPrefab;
|
||||
public Transform effectSpawnPoint;
|
||||
|
||||
private NetworkVariable<int> _currentResources = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private float _lastGatheringTime;
|
||||
private float _lastRechargeTime;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
_currentResources.Value = 0;
|
||||
_lastRechargeTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer)
|
||||
return;
|
||||
|
||||
// 자원 충전 로직
|
||||
if (Time.time - _lastRechargeTime >= rechargeInterval)
|
||||
{
|
||||
if (_currentResources.Value < maxResources)
|
||||
{
|
||||
int rechargeAmountToAdd = Mathf.Min(rechargeAmount, maxResources - _currentResources.Value);
|
||||
_currentResources.Value += rechargeAmountToAdd;
|
||||
|
||||
Debug.Log($"{resourceName} {rechargeAmountToAdd} 충전됨. 현재: {_currentResources.Value}/{maxResources}");
|
||||
}
|
||||
|
||||
_lastRechargeTime = Time.time;
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
// 자원 노드에 자원이 없으면 상호작용 불가
|
||||
if (_currentResources.Value <= 0)
|
||||
return false;
|
||||
|
||||
// 쿨다운 확인
|
||||
if (Time.time - _lastGatheringTime < gatheringCooldown)
|
||||
return false;
|
||||
|
||||
// 플레이어 인벤토리 확인
|
||||
if (NetworkManager.Singleton != null &&
|
||||
NetworkManager.Singleton.ConnectedClients.TryGetValue(playerId, out var client))
|
||||
{
|
||||
if (client.PlayerObject != null)
|
||||
{
|
||||
var playerInventory = client.PlayerObject.GetComponent<PlayerResourceInventory>();
|
||||
if (playerInventory != null)
|
||||
{
|
||||
// 플레이어가 받을 수 있는 공간이 없으면 상호작용 불가
|
||||
if (playerInventory.GetAvailableSpace() <= 0)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
GatherResourceServerRpc(playerId);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void GatherResourceServerRpc(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
// 플레이어의 인벤토리 확인
|
||||
var playerObject = NetworkManager.Singleton.ConnectedClients[playerId].PlayerObject;
|
||||
if (playerObject == null)
|
||||
return;
|
||||
|
||||
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
|
||||
if (playerInventory == null)
|
||||
{
|
||||
Debug.LogWarning($"플레이어 {playerId}에게 PlayerResourceInventory 컴포넌트가 없습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 플레이어가 받을 수 있는 최대량 계산
|
||||
int playerAvailableSpace = playerInventory.GetAvailableSpace();
|
||||
|
||||
// 자원 노드가 줄 수 있는 양과 플레이어가 받을 수 있는 양 중 작은 값 선택
|
||||
int gatheredAmount = Mathf.Min(
|
||||
resourcesPerGathering,
|
||||
_currentResources.Value,
|
||||
playerAvailableSpace
|
||||
);
|
||||
|
||||
if (gatheredAmount <= 0)
|
||||
{
|
||||
Debug.Log($"플레이어 {playerId}의 인벤토리가 가득 찼습니다.");
|
||||
return;
|
||||
}
|
||||
|
||||
// 자원 차감
|
||||
_currentResources.Value -= gatheredAmount;
|
||||
_lastGatheringTime = Time.time;
|
||||
|
||||
// 플레이어에게 자원 추가
|
||||
playerInventory.AddResourceServerRpc(gatheredAmount);
|
||||
|
||||
Debug.Log($"플레이어 {playerId}가 {gatheredAmount} {resourceName}을(를) 채집했습니다. 남은 자원: {_currentResources.Value}");
|
||||
|
||||
ShowGatheringEffectClientRpc();
|
||||
}
|
||||
|
||||
[Rpc(SendTo.ClientsAndHost)]
|
||||
private void ShowGatheringEffectClientRpc()
|
||||
{
|
||||
if (gatheringEffectPrefab != null && effectSpawnPoint != null)
|
||||
{
|
||||
GameObject effect = Instantiate(gatheringEffectPrefab, effectSpawnPoint.position, effectSpawnPoint.rotation);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
if (_currentResources.Value <= 0)
|
||||
return "자원 충전 중...";
|
||||
|
||||
return $"[E] {resourceName} 채집 ({_currentResources.Value}/{maxResources})";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
return interactionAnimationTrigger;
|
||||
}
|
||||
|
||||
public InteractionEquipmentData GetEquipmentData()
|
||||
{
|
||||
return equipmentData;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Resource.cs.meta
Normal file
2
Assets/Scripts/Resource.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 13db2a1998ede61439aeb8b967160b5e
|
||||
Reference in New Issue
Block a user