일꾼(Worker) 개발 및 Kaykit Adventurer 애셋 추가
코어 오른쪽 건물에서 상호작용 하면 Worker 생성 Worker와 상호작용하면 Worker가 플레이어를 따라옴 그 상태에서 자원과 상호작용하면 Worker를 자원에 배치할 수 있음.
This commit is contained in:
227
Assets/Scripts/WorkerSpawner.cs
Normal file
227
Assets/Scripts/WorkerSpawner.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class WorkerSpawner : NetworkBehaviour, IInteractable
|
||||
{
|
||||
[Header("Spawner Settings")]
|
||||
public GameObject workerPrefab;
|
||||
public Transform spawnPoint;
|
||||
public float spawnRadius = 2f;
|
||||
public int maxWorkers = 5;
|
||||
public float spawnCooldown = 5f;
|
||||
|
||||
[Header("Interaction")]
|
||||
public string interactionAnimationTrigger = "Build";
|
||||
public string interactionPrompt = "[E] 워커 생성";
|
||||
|
||||
[Header("Visual")]
|
||||
public GameObject spawnEffectPrefab;
|
||||
|
||||
private NetworkVariable<int> _workerCount = new NetworkVariable<int>(
|
||||
0,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server
|
||||
);
|
||||
|
||||
private float _lastSpawnTime;
|
||||
|
||||
public int WorkerCount => _workerCount.Value;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
base.OnNetworkSpawn();
|
||||
_workerCount.OnValueChanged += OnWorkerCountChanged;
|
||||
}
|
||||
|
||||
public override void OnNetworkDespawn()
|
||||
{
|
||||
_workerCount.OnValueChanged -= OnWorkerCountChanged;
|
||||
base.OnNetworkDespawn();
|
||||
}
|
||||
|
||||
private void OnWorkerCountChanged(int previousValue, int newValue)
|
||||
{
|
||||
Debug.Log($"[WorkerSpawner] 워커 수 변경: {previousValue} → {newValue}");
|
||||
}
|
||||
|
||||
#region IInteractable Implementation
|
||||
|
||||
public bool CanInteract(ulong playerId)
|
||||
{
|
||||
if (_workerCount.Value >= maxWorkers)
|
||||
return false;
|
||||
|
||||
if (Time.time - _lastSpawnTime < spawnCooldown)
|
||||
return false;
|
||||
|
||||
if (workerPrefab == null)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Interact(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
SpawnWorkerServerRpc(playerId);
|
||||
}
|
||||
|
||||
[Rpc(SendTo.Server, InvokePermission = RpcInvokePermission.Everyone)]
|
||||
private void SpawnWorkerServerRpc(ulong playerId)
|
||||
{
|
||||
if (!CanInteract(playerId))
|
||||
return;
|
||||
|
||||
if (_workerCount.Value >= maxWorkers)
|
||||
{
|
||||
Debug.LogWarning("[WorkerSpawner] 최대 워커 수에 도달함");
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 spawnPosition = spawnPoint != null ? spawnPoint.position : transform.position;
|
||||
|
||||
float randomAngle = Random.Range(0f, 360f);
|
||||
Vector3 offset = new Vector3(
|
||||
Mathf.Cos(randomAngle * Mathf.Deg2Rad) * spawnRadius,
|
||||
0f,
|
||||
Mathf.Sin(randomAngle * Mathf.Deg2Rad) * spawnRadius
|
||||
);
|
||||
spawnPosition += offset;
|
||||
|
||||
GameObject workerObj = Instantiate(workerPrefab, spawnPosition, Quaternion.identity);
|
||||
NetworkObject workerNetObj = workerObj.GetComponent<NetworkObject>();
|
||||
|
||||
if (workerNetObj == null)
|
||||
{
|
||||
Debug.LogError("[WorkerSpawner] Worker prefab must have NetworkObject component");
|
||||
Destroy(workerObj);
|
||||
return;
|
||||
}
|
||||
|
||||
workerNetObj.Spawn();
|
||||
_lastSpawnTime = Time.time;
|
||||
_workerCount.Value++;
|
||||
|
||||
ShowSpawnEffectClientRpc(spawnPosition);
|
||||
|
||||
Debug.Log($"[WorkerSpawner] 워커 생성됨 (총 워커: {_workerCount.Value}/{maxWorkers})");
|
||||
|
||||
ScheduleWorkerAssignment(playerId, workerNetObj.NetworkObjectId);
|
||||
}
|
||||
|
||||
private void ScheduleWorkerAssignment(ulong playerId, ulong workerNetObjectId)
|
||||
{
|
||||
StartCoroutine(AssignWorkerAfterFrame(playerId, workerNetObjectId));
|
||||
}
|
||||
|
||||
private System.Collections.IEnumerator AssignWorkerAfterFrame(ulong playerId, ulong workerNetObjectId)
|
||||
{
|
||||
yield return new WaitForEndOfFrame();
|
||||
|
||||
if (NetworkManager.Singleton.SpawnManager.SpawnedObjects.TryGetValue(workerNetObjectId, out var workerObj))
|
||||
{
|
||||
var worker = workerObj.GetComponent<Worker>();
|
||||
if (worker != null)
|
||||
{
|
||||
SetPlayerAssignedWorkerClientRpc(playerId, workerNetObjectId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.ClientsAndHost)]
|
||||
private void SetPlayerAssignedWorkerClientRpc(ulong playerId, ulong workerNetObjectId)
|
||||
{
|
||||
var spawnedObjects = NetworkManager.Singleton.SpawnManager.SpawnedObjects;
|
||||
|
||||
NetworkObject playerObj = null;
|
||||
foreach (var kvp in spawnedObjects)
|
||||
{
|
||||
if (kvp.Value != null && kvp.Value.OwnerClientId == playerId)
|
||||
{
|
||||
playerObj = kvp.Value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (playerObj != null)
|
||||
{
|
||||
var playerInteraction = playerObj.GetComponent<PlayerInteraction>();
|
||||
if (playerInteraction != null && playerInteraction.IsOwner)
|
||||
{
|
||||
if (spawnedObjects.TryGetValue(workerNetObjectId, out var workerObj))
|
||||
{
|
||||
var worker = workerObj.GetComponent<Worker>();
|
||||
if (worker != null)
|
||||
{
|
||||
playerInteraction.assignedWorker = worker;
|
||||
Debug.Log($"[WorkerSpawner] Worker assigned to player {playerId}'s PlayerInteraction");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Rpc(SendTo.ClientsAndHost)]
|
||||
private void ShowSpawnEffectClientRpc(Vector3 position)
|
||||
{
|
||||
if (spawnEffectPrefab != null)
|
||||
{
|
||||
GameObject effect = Instantiate(spawnEffectPrefab, position, Quaternion.identity);
|
||||
Destroy(effect, 2f);
|
||||
}
|
||||
}
|
||||
|
||||
public string GetInteractionPrompt()
|
||||
{
|
||||
if (_workerCount.Value >= maxWorkers)
|
||||
return $"워커 수 최대 ({_workerCount.Value}/{maxWorkers})";
|
||||
|
||||
float cooldownRemaining = Mathf.Max(0, spawnCooldown - (Time.time - _lastSpawnTime));
|
||||
if (cooldownRemaining > 0)
|
||||
return $"워커 생성 대기 중 ({cooldownRemaining:F1}s)";
|
||||
|
||||
return $"{interactionPrompt} ({_workerCount.Value}/{maxWorkers})";
|
||||
}
|
||||
|
||||
public string GetInteractionAnimation()
|
||||
{
|
||||
return interactionAnimationTrigger;
|
||||
}
|
||||
|
||||
public EquipmentData GetEquipmentData()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Transform GetTransform()
|
||||
{
|
||||
return transform;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public void OnWorkerDestroyed()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
_workerCount.Value = Mathf.Max(0, _workerCount.Value - 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
Gizmos.color = Color.green;
|
||||
Vector3 spawnCenter = spawnPoint != null ? spawnPoint.position : transform.position;
|
||||
Gizmos.DrawWireSphere(spawnCenter, spawnRadius);
|
||||
|
||||
UnityEditor.Handles.Label(spawnCenter + Vector3.up * 2f,
|
||||
$"Worker Spawner\nWorkers: {_workerCount.Value}/{maxWorkers}");
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user