Files
Northbound/Assets/Scripts/Resource.cs
dal4segno 10b496dfae 네트워크 멀티플레이 환경 문제 수정
관련 문제가 다시 발생하면 이 커밋으로 돌아올 것
2026-02-02 04:24:14 +09:00

279 lines
8.5 KiB
C#

using Unity.Netcode;
using UnityEngine;
using System.Collections.Generic;
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 EquipmentData equipmentData = new EquipmentData
{
socketName = "RightHand",
equipmentPrefab = null, // Inspector에서 곡괭이 프리팹 할당
attachOnStart = true,
detachOnEnd = true
};
[Header("Visual")]
public GameObject gatheringEffectPrefab;
public Transform effectSpawnPoint;
[Header("Worker Assignment")]
public bool allowWorkerAssignment = true;
[Header("Multi-worker")]
public bool allowMultipleWorkers = false; // 한 자원에 여러 워커 허용 여부
public bool HasResourcesAvailable()
{
return _currentResources.Value > 0;
}
public bool CanWorkerMineResource(ulong workerId)
{
if (!HasResourcesAvailable())
return false;
if (allowMultipleWorkers)
return true;
if (_currentWorkerId.Value == ulong.MaxValue)
return true;
return _currentWorkerId.Value == workerId;
}
public void TakeResourcesForWorker(int amount, ulong workerId)
{
if (!IsServer) return;
if (!allowMultipleWorkers)
{
if (_currentWorkerId.Value != ulong.MaxValue && _currentWorkerId.Value != workerId)
return;
}
int availableResources = _currentResources.Value;
int actualAmount = Mathf.Min(amount, availableResources);
if (actualAmount <= 0)
return;
_currentResources.Value -= actualAmount;
_lastGatheringTime = Time.time;
if (!allowMultipleWorkers && actualAmount > 0)
{
_currentWorkerId.Value = workerId;
}
if (_currentResources.Value <= 0 && !allowMultipleWorkers)
{
_currentWorkerId.Value = ulong.MaxValue;
}
ShowGatheringEffectClientRpc();
}
private NetworkVariable<int> _currentResources = new NetworkVariable<int>(
0,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
private NetworkVariable<ulong> _currentWorkerId = new NetworkVariable<ulong>(
ulong.MaxValue,
NetworkVariableReadPermission.Everyone,
NetworkVariableWritePermission.Server
);
private float _lastGatheringTime;
private float _lastRechargeTime;
public override void OnNetworkSpawn()
{
if (IsServer)
{
_currentResources.Value = maxResources;
_lastRechargeTime = Time.time;
_lastGatheringTime = Time.time - gatheringCooldown;
}
}
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;
var resourceManager = ServerResourceManager.Instance;
if (resourceManager != null)
{
if (resourceManager.GetAvailableSpace(playerId) <= 0)
return false;
}
return true;
}
public void Interact(ulong playerId)
{
AssignOrGatherResourceServerRpc(playerId, NetworkObject.NetworkObjectId);
}
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
private void AssignOrGatherResourceServerRpc(ulong playerId, ulong resourceId)
{
var playerObject = NetworkManager.Singleton.ConnectedClients[playerId].PlayerObject;
if (playerObject == null)
{
return;
}
var playerInteraction = playerObject.GetComponent<PlayerInteraction>();
bool workerAssigned = false;
if (allowWorkerAssignment && playerInteraction != null && playerInteraction.assignedWorker != null)
{
var worker = playerInteraction.assignedWorker;
if (worker.OwnerPlayerId == playerId && (int)worker.CurrentState == 1)
{
worker.AssignMiningTargetServerRpc(resourceId);
workerAssigned = true;
ShowGatheringEffectClientRpc();
}
}
if (!workerAssigned)
{
if (!CanInteract(playerId))
{
return;
}
GatherResource(playerId);
}
}
private void GatherResource(ulong playerId)
{
if (!CanInteract(playerId))
return;
var resourceManager = ServerResourceManager.Instance;
if (resourceManager == null)
return;
int playerAvailableSpace = resourceManager.GetAvailableSpace(playerId);
int gatheredAmount = Mathf.Min(
resourcesPerGathering,
_currentResources.Value,
playerAvailableSpace
);
if (gatheredAmount <= 0)
{
return;
}
_currentResources.Value -= gatheredAmount;
_lastGatheringTime = Time.time;
resourceManager.AddResource(playerId, gatheredAmount);
UpdatePlayerResourcesClientRpc(playerId);
ShowGatheringEffectClientRpc();
}
[Rpc(SendTo.ClientsAndHost)]
private void UpdatePlayerResourcesClientRpc(ulong playerId)
{
var playerObject = NetworkManager.Singleton.ConnectedClients[playerId].PlayerObject;
if (playerObject != null)
{
var playerInventory = playerObject.GetComponent<PlayerResourceInventory>();
if (playerInventory != null)
{
playerInventory.RequestResourceUpdateServerRpc();
}
}
}
[Rpc(SendTo.NotServer)]
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 EquipmentData GetEquipmentData()
{
return equipmentData;
}
public Transform GetTransform()
{
return transform;
}
}
}