From 95db5ce4f3a0e38b00a8972047774f7d079f9489 Mon Sep 17 00:00:00 2001 From: dal4segno Date: Mon, 16 Feb 2026 16:31:46 +0900 Subject: [PATCH] =?UTF-8?q?=ED=81=AC=EB=A6=BD=20=EC=BA=A0=ED=94=84?= =?UTF-8?q?=EC=9D=98=20=EB=AA=A8=EB=93=A0=20=ED=81=AC=EB=A6=BD=20=EC=B2=98?= =?UTF-8?q?=EC=B9=98=20=EC=8B=9C=20=ED=9A=8D=EB=93=9D=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=9C=20=EC=9E=90=EC=9B=90=EC=9D=B4=20=EC=86=8C=ED=99=98?= =?UTF-8?q?=EB=90=98=EB=8F=84=EB=A1=9D=20=ED=95=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Assets/Prefabs/CreepCamp.prefab | 4 +- Assets/Scripts/CreepCamp.cs | 131 +++++++++++++++++++++++++++++++- Assets/Scripts/MapGenerator.cs | 3 +- 3 files changed, 133 insertions(+), 5 deletions(-) diff --git a/Assets/Prefabs/CreepCamp.prefab b/Assets/Prefabs/CreepCamp.prefab index ad1edc4..6898ebc 100644 --- a/Assets/Prefabs/CreepCamp.prefab +++ b/Assets/Prefabs/CreepCamp.prefab @@ -45,7 +45,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: d5a57f767e5e46a458fc5d3c628d0cbb, type: 3} m_Name: m_EditorClassIdentifier: Unity.Netcode.Runtime::Unity.Netcode.NetworkObject - GlobalObjectIdHash: 1167141433 + GlobalObjectIdHash: 1920228393 InScenePlacedSourceGlobalObjectIdHash: 0 DeferredDespawnTick: 0 Ownership: 1 @@ -72,3 +72,5 @@ MonoBehaviour: m_EditorClassIdentifier: Assembly-CSharp::Northbound.CreepCamp ShowTopMostFoldoutHeaderGroup: 1 creepPrefabs: [] + resourcePickupPrefab: {fileID: 1627676033990080135, guid: 8c45964a69bf8fa4ba461ed217bc052f, type: 3} + baseResourceAmount: 50 diff --git a/Assets/Scripts/CreepCamp.cs b/Assets/Scripts/CreepCamp.cs index 4488290..06497cc 100644 --- a/Assets/Scripts/CreepCamp.cs +++ b/Assets/Scripts/CreepCamp.cs @@ -11,12 +11,23 @@ namespace Northbound [Tooltip("Creep prefabs available to spawn")] [SerializeField] private List creepPrefabs = new List(); + [Header("Reward Settings")] + [Tooltip("Resource pickup prefab to spawn when all creeps are defeated")] + [SerializeField] private GameObject resourcePickupPrefab; + + [Tooltip("Base resource amount multiplier (actual amount = this * camp strength)")] + [SerializeField] private int baseResourceAmount = 50; + private float _zPosition; private float _campStrength; private float _campCostBudget; private float _spawnRadius; private int _maxSpawnAttempts; + private readonly List _spawnedCreeps = new List(); + private ResourcePickup _resourcePickup; + private readonly Dictionary> _deathHandlers = new Dictionary>(); + public override void OnNetworkSpawn() { if (IsServer) @@ -25,6 +36,27 @@ namespace Northbound } } + public override void OnNetworkDespawn() + { + base.OnNetworkDespawn(); + + if (IsServer) + { + // 모든 이벤트 구독 해제 + foreach (var kvp in _deathHandlers) + { + if (kvp.Key != null && kvp.Value != null) + { + kvp.Key.OnDeath -= kvp.Value; + } + } + + // 리스트와 딕셔너리 비우기 + _spawnedCreeps.Clear(); + _deathHandlers.Clear(); + } + } + public void InitializeCamp(float zPosition, float strengthMultiplier, float costBudget, float radius, int maxAttempts) { _zPosition = zPosition; @@ -42,15 +74,16 @@ namespace Northbound private void SpawnCreeps() { - if (creepPrefabs.Count == 0) { Debug.LogWarning($"[CreepCamp] No creep prefabs assigned!"); return; } - float remainingCost = _campCostBudget * _campStrength; + // 리소스 픽업 스폰 (비활성화 상태로) + SpawnResourcePickup(); + float remainingCost = _campCostBudget * _campStrength; int spawnedCount = 0; for (int attempt = 0; attempt < _maxSpawnAttempts && remainingCost > 0; attempt++) @@ -142,6 +175,85 @@ namespace Northbound return false; } + private void SpawnResourcePickup() + { + if (resourcePickupPrefab == null) + { + Debug.LogWarning($"[CreepCamp] No resource pickup prefab assigned!"); + return; + } + + GameObject pickup = Instantiate(resourcePickupPrefab, transform.position, Quaternion.identity); + + _resourcePickup = pickup.GetComponent(); + if (_resourcePickup == null) + { + Debug.LogError($"[CreepCamp] ResourcePickup component not found on prefab!"); + Destroy(pickup); + return; + } + + // 캠프 강도에 비례하여 리소스 양 설정 + _resourcePickup.resourceAmount = Mathf.RoundToInt(baseResourceAmount * _campStrength); + + // NetworkObject 추가 및 스폰 + NetworkObject networkObj = pickup.GetComponent(); + if (networkObj == null) + { + networkObj = pickup.AddComponent(); + } + + networkObj.SpawnWithOwnership(NetworkManager.Singleton.LocalClientId); + + // 비활성화는 ServerRpc를 통해 처리 + DisablePickupClientRpc(); + + Debug.Log($"[CreepCamp] Resource pickup spawned (Amount: {_resourcePickup.resourceAmount})"); + } + + private void HandleCreepDeath(EnemyUnit deadCreep) + { + // 이벤트 구독 해제 (메모리 누수 방지) + if (_deathHandlers.ContainsKey(deadCreep)) + { + if (deadCreep != null) + { + deadCreep.OnDeath -= _deathHandlers[deadCreep]; + } + _deathHandlers.Remove(deadCreep); + } + + // 리스트에서 해당 creep 제거 + _spawnedCreeps.Remove(deadCreep); + + Debug.Log($"[CreepCamp] Creep died. Remaining creeps: {_spawnedCreeps.Count}"); + + // 모든 creep이 처치되었으면 ResourcePickup 활성화 + if (_spawnedCreeps.Count == 0 && _resourcePickup != null) + { + EnableResourcePickupClientRpc(); + Debug.Log($"[CreepCamp] All creeps defeated! Resource pickup enabled."); + } + } + + [Rpc(SendTo.ClientsAndHost)] + private void EnableResourcePickupClientRpc() + { + if (_resourcePickup != null) + { + _resourcePickup.gameObject.SetActive(true); + } + } + + [Rpc(SendTo.ClientsAndHost)] + private void DisablePickupClientRpc() + { + if (_resourcePickup != null) + { + _resourcePickup.gameObject.SetActive(false); + } + } + private CreepData GetCreepDataFromPrefab(GameObject prefab) { if (prefab == null) return null; @@ -175,6 +287,21 @@ namespace Northbound networkObj = creep.AddComponent(); } + // EnemyUnit 참조 저장 및 이벤트 구독 + EnemyUnit enemyUnit = creep.GetComponent(); + if (enemyUnit != null) + { + _spawnedCreeps.Add(enemyUnit); + // Dictionary에 핸들러 저장 (메모리 누수 방지) + System.Action handler = (killerId) => HandleCreepDeath(enemyUnit); + _deathHandlers[enemyUnit] = handler; + enemyUnit.OnDeath += handler; + } + else + { + Debug.LogWarning($"[CreepCamp] EnemyUnit component not found on creep prefab {prefab.name}"); + } + networkObj.SpawnWithOwnership(NetworkManager.Singleton.LocalClientId); } diff --git a/Assets/Scripts/MapGenerator.cs b/Assets/Scripts/MapGenerator.cs index a302f86..4242857 100644 --- a/Assets/Scripts/MapGenerator.cs +++ b/Assets/Scripts/MapGenerator.cs @@ -567,11 +567,10 @@ namespace Northbound private Vector3 GetRandomPositionInPlayableArea() { - Vector3 center = transform.position; float x = Random.Range(-playableAreaWidth / 2f, playableAreaWidth / 2f); float z = Random.Range(startZ, endZ); - Vector3 result = new Vector3(x, center.y, z); + Vector3 result = new Vector3(x, 1, z); return result; }