데이터 파이프라인 추가 및 수정 + 크립 및 크립 캠프 배치 자동화
Hierachy > System > MapGenerator 에서 크립 관련 변수 설정 가능 Kaykit PlantWarrior 애셋 추가
This commit is contained in:
@@ -1,69 +0,0 @@
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
/// <summary>
|
||||
/// 건물에 데미지를 주는 테스트/공격 시스템
|
||||
/// </summary>
|
||||
public class BuildingDamageTest : MonoBehaviour
|
||||
{
|
||||
[Header("Damage Settings")]
|
||||
public int damageAmount = 10;
|
||||
public float damageInterval = 1f;
|
||||
public KeyCode damageKey = KeyCode.F;
|
||||
|
||||
[Header("Target")]
|
||||
public float maxDistance = 10f;
|
||||
public LayerMask buildingLayer;
|
||||
|
||||
private float _lastDamageTime;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (Input.GetKeyDown(damageKey))
|
||||
{
|
||||
TryDamageBuilding();
|
||||
}
|
||||
|
||||
// 자동 데미지 (테스트용)
|
||||
if (Input.GetKey(KeyCode.LeftShift) && Input.GetKey(damageKey))
|
||||
{
|
||||
if (Time.time - _lastDamageTime >= damageInterval)
|
||||
{
|
||||
TryDamageBuilding();
|
||||
_lastDamageTime = Time.time;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void TryDamageBuilding()
|
||||
{
|
||||
// 카메라에서 레이캐스트
|
||||
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||||
|
||||
if (Physics.Raycast(ray, out RaycastHit hit, maxDistance, buildingLayer))
|
||||
{
|
||||
Building building = hit.collider.GetComponentInParent<Building>();
|
||||
|
||||
if (building != null)
|
||||
{
|
||||
ulong attackerId = NetworkManager.Singleton != null && NetworkManager.Singleton.IsClient
|
||||
? NetworkManager.Singleton.LocalClientId
|
||||
: 0;
|
||||
|
||||
building.TakeDamage(damageAmount, attackerId);
|
||||
Debug.Log($"<color=yellow>[Test] {building.buildingData?.buildingName ?? "건물"}에게 {damageAmount} 데미지!</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.BeginArea(new Rect(10, 10, 300, 100));
|
||||
GUILayout.Label($"[{damageKey}] 건물에 {damageAmount} 데미지");
|
||||
GUILayout.Label($"[Shift + {damageKey}] 연속 데미지");
|
||||
GUILayout.EndArea();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 62b3bdffc8c849d48886167d316e16b6
|
||||
@@ -60,6 +60,7 @@ namespace Northbound
|
||||
if (buildingData == null) return;
|
||||
|
||||
// 아이콘 설정
|
||||
/*
|
||||
if (iconImage != null && buildingData.icon != null)
|
||||
{
|
||||
iconImage.sprite = buildingData.icon;
|
||||
@@ -69,7 +70,8 @@ namespace Northbound
|
||||
{
|
||||
iconImage.enabled = false;
|
||||
}
|
||||
|
||||
*/
|
||||
iconImage.enabled = false;
|
||||
// 이름 설정
|
||||
if (nameText != null)
|
||||
{
|
||||
|
||||
215
Assets/Scripts/CreepCamp.cs
Normal file
215
Assets/Scripts/CreepCamp.cs
Normal file
@@ -0,0 +1,215 @@
|
||||
using Northbound.Data;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
public class CreepCamp : NetworkBehaviour
|
||||
{
|
||||
[Header("Camp Settings")]
|
||||
[Tooltip("Creep prefabs available to spawn")]
|
||||
[SerializeField] private List<GameObject> creepPrefabs = new List<GameObject>();
|
||||
|
||||
[Header("Spawning Settings")]
|
||||
[Tooltip("Camp strength multiplier (higher = stronger creeps)")]
|
||||
[SerializeField] private float campStrength = 1f;
|
||||
|
||||
[Tooltip("Cost budget for spawning creeps")]
|
||||
[SerializeField] private float campCostBudget = 10f;
|
||||
|
||||
[Tooltip("Spawn radius around camp")]
|
||||
[SerializeField] private float spawnRadius = 5f;
|
||||
|
||||
[Tooltip("Max creep spawn attempts")]
|
||||
[SerializeField] private int maxSpawnAttempts = 50;
|
||||
|
||||
private float _zPosition;
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (IsServer)
|
||||
{
|
||||
SpawnCreeps();
|
||||
}
|
||||
}
|
||||
|
||||
public void InitializeCamp(float zPosition, float strengthMultiplier)
|
||||
{
|
||||
_zPosition = zPosition;
|
||||
campStrength = strengthMultiplier;
|
||||
}
|
||||
|
||||
public void SetCreepPrefabs(List<GameObject> prefabs)
|
||||
{
|
||||
creepPrefabs.Clear();
|
||||
creepPrefabs.AddRange(prefabs);
|
||||
Debug.Log($"[CreepCamp] SetCreepPrefabs called with {prefabs.Count} prefabs. Current count: {creepPrefabs.Count}");
|
||||
}
|
||||
|
||||
private void SpawnCreeps()
|
||||
{
|
||||
Debug.Log($"[CreepCamp] Starting creep spawn at Z={_zPosition:F1}, strength={campStrength:F2}x, prefabs={creepPrefabs.Count}");
|
||||
|
||||
if (creepPrefabs.Count == 0)
|
||||
{
|
||||
Debug.LogWarning($"[CreepCamp] No creep prefabs assigned!");
|
||||
return;
|
||||
}
|
||||
|
||||
float remainingCost = campCostBudget * campStrength;
|
||||
Debug.Log($"[CreepCamp] Cost budget: {campCostBudget} x {campStrength:F2} = {remainingCost:F2}");
|
||||
|
||||
int spawnedCount = 0;
|
||||
|
||||
for (int attempt = 0; attempt < maxSpawnAttempts && remainingCost > 0; attempt++)
|
||||
{
|
||||
GameObject selectedCreep = SelectCreepByCost(remainingCost);
|
||||
if (selectedCreep == null)
|
||||
{
|
||||
Debug.LogWarning($"[CreepCamp] Could not select creep (attempt {attempt + 1}/{maxSpawnAttempts}), remaining cost: {remainingCost:F2}");
|
||||
break;
|
||||
}
|
||||
|
||||
CreepData creepData = GetCreepDataFromPrefab(selectedCreep);
|
||||
if (creepData == null)
|
||||
{
|
||||
Debug.LogWarning($"[CreepCamp] Could not get creep data from {selectedCreep.name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (creepData.cost > remainingCost)
|
||||
{
|
||||
if (!CanSpawnAnyCreep(remainingCost))
|
||||
{
|
||||
Debug.LogWarning($"[CreepCamp] No affordable creeps found. Remaining cost: {remainingCost:F2}");
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
Debug.Log($"[CreepCamp] Spawning {selectedCreep.name} (cost: {creepData.cost}, remaining: {remainingCost:F2})");
|
||||
SpawnCreep(selectedCreep);
|
||||
remainingCost -= creepData.cost;
|
||||
spawnedCount++;
|
||||
}
|
||||
|
||||
Debug.Log($"[CreepCamp] Spawned {spawnedCount} creeps at Z={_zPosition:F1} with strength {campStrength:F2}x");
|
||||
}
|
||||
|
||||
private GameObject SelectCreepByCost(float remainingCost)
|
||||
{
|
||||
List<GameObject> affordableCreeps = new List<GameObject>();
|
||||
|
||||
foreach (var prefab in creepPrefabs)
|
||||
{
|
||||
CreepData creepData = GetCreepDataFromPrefab(prefab);
|
||||
if (creepData != null && creepData.cost <= remainingCost)
|
||||
{
|
||||
affordableCreeps.Add(prefab);
|
||||
}
|
||||
}
|
||||
|
||||
if (affordableCreeps.Count == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
float totalWeight = 0f;
|
||||
foreach (var prefab in affordableCreeps)
|
||||
{
|
||||
CreepData creepData = GetCreepDataFromPrefab(prefab);
|
||||
if (creepData != null)
|
||||
{
|
||||
totalWeight += creepData.weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (totalWeight == 0f)
|
||||
{
|
||||
return affordableCreeps[Random.Range(0, affordableCreeps.Count)];
|
||||
}
|
||||
|
||||
float randomValue = Random.Range(0f, totalWeight);
|
||||
float cumulativeWeight = 0f;
|
||||
|
||||
foreach (var prefab in affordableCreeps)
|
||||
{
|
||||
CreepData creepData = GetCreepDataFromPrefab(prefab);
|
||||
if (creepData != null)
|
||||
{
|
||||
cumulativeWeight += creepData.weight;
|
||||
if (randomValue <= cumulativeWeight)
|
||||
{
|
||||
return prefab;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return affordableCreeps[Random.Range(0, affordableCreeps.Count)];
|
||||
}
|
||||
|
||||
private bool CanSpawnAnyCreep(float remainingCost)
|
||||
{
|
||||
foreach (var prefab in creepPrefabs)
|
||||
{
|
||||
CreepData creepData = GetCreepDataFromPrefab(prefab);
|
||||
if (creepData != null && creepData.cost <= remainingCost)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private CreepData GetCreepDataFromPrefab(GameObject prefab)
|
||||
{
|
||||
if (prefab == null) return null;
|
||||
|
||||
CreepDataComponent component = prefab.GetComponent<CreepDataComponent>();
|
||||
if (component != null)
|
||||
{
|
||||
return component.creepData;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SpawnCreep(GameObject prefab)
|
||||
{
|
||||
Vector3 spawnOffset = Random.insideUnitSphere * spawnRadius;
|
||||
spawnOffset.y = 0;
|
||||
|
||||
GameObject creep = Instantiate(prefab, transform.position + spawnOffset, Quaternion.identity);
|
||||
|
||||
if (creep.GetComponent<FogOfWarVisibility>() == null)
|
||||
{
|
||||
var visibility = creep.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = false;
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
|
||||
NetworkObject networkObj = creep.GetComponent<NetworkObject>();
|
||||
if (networkObj == null)
|
||||
{
|
||||
networkObj = creep.AddComponent<NetworkObject>();
|
||||
}
|
||||
|
||||
networkObj.Spawn();
|
||||
|
||||
creep.transform.SetParent(transform);
|
||||
}
|
||||
|
||||
private void OnDrawGizmos()
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
Gizmos.DrawWireSphere(transform.position, spawnRadius);
|
||||
}
|
||||
|
||||
private void OnDrawGizmosSelected()
|
||||
{
|
||||
Gizmos.color = new Color(1f, 0f, 0f, 0.3f);
|
||||
Gizmos.DrawSphere(transform.position, spawnRadius);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/CreepCamp.cs.meta
Normal file
2
Assets/Scripts/CreepCamp.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d4c5f296c2ebd7d41b2517a03c8b574c
|
||||
62
Assets/Scripts/CreepDataComponent.cs
Normal file
62
Assets/Scripts/CreepDataComponent.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Northbound.Data;
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
namespace Northbound
|
||||
{
|
||||
[RequireComponent(typeof(EnemyUnit))]
|
||||
[RequireComponent(typeof(EnemyAIController))]
|
||||
[RequireComponent(typeof(NavMeshAgent))]
|
||||
[RequireComponent(typeof(NetworkObject))]
|
||||
public class CreepDataComponent : MonoBehaviour
|
||||
{
|
||||
[Header("Data Reference")]
|
||||
[Tooltip("ScriptableObject containing creep data")]
|
||||
public CreepData creepData;
|
||||
|
||||
[Header("Auto-Apply Settings")]
|
||||
[Tooltip("Automatically apply stats from creepData on Awake")]
|
||||
public bool autoApplyOnAwake = true;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (autoApplyOnAwake && creepData != null)
|
||||
{
|
||||
ApplyCreepData();
|
||||
}
|
||||
}
|
||||
|
||||
public void ApplyCreepData()
|
||||
{
|
||||
if (creepData == null)
|
||||
{
|
||||
Debug.LogWarning("[CreepDataComponent] creepData is null", this);
|
||||
return;
|
||||
}
|
||||
|
||||
EnemyUnit enemyUnit = GetComponent<EnemyUnit>();
|
||||
if (enemyUnit != null)
|
||||
{
|
||||
enemyUnit.maxHealth = creepData.maxHp;
|
||||
}
|
||||
|
||||
EnemyAIController aiController = GetComponent<EnemyAIController>();
|
||||
if (aiController != null)
|
||||
{
|
||||
aiController.moveSpeed = creepData.moveSpeed;
|
||||
aiController.attackDamage = creepData.atkDamage;
|
||||
aiController.attackRange = creepData.atkRange;
|
||||
aiController.attackInterval = creepData.atkIntervalSec;
|
||||
}
|
||||
|
||||
NavMeshAgent navAgent = GetComponent<NavMeshAgent>();
|
||||
if (navAgent != null)
|
||||
{
|
||||
navAgent.speed = creepData.moveSpeed;
|
||||
}
|
||||
|
||||
Debug.Log($"[CreepDataComponent] Applied data for {creepData.id} ({creepData.memo})", this);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/CreepDataComponent.cs.meta
Normal file
2
Assets/Scripts/CreepDataComponent.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 85e4c68e85ef1704b83cecb71de7d67a
|
||||
@@ -59,6 +59,7 @@ namespace Northbound.Editor
|
||||
prefabSetups = new Dictionary<string, IPrefabSetup>
|
||||
{
|
||||
{ "Monster", new MonsterPrefabSetup() },
|
||||
{ "Creep", new CreepPrefabSetup() },
|
||||
{ "Tower", new TowerPrefabSetup() },
|
||||
{ "Player", new PlayerPrefabSetup() }
|
||||
};
|
||||
|
||||
118
Assets/Scripts/Editor/CreepPrefabSetup.cs
Normal file
118
Assets/Scripts/Editor/CreepPrefabSetup.cs
Normal file
@@ -0,0 +1,118 @@
|
||||
using Northbound.Data;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Northbound.Editor
|
||||
{
|
||||
public class CreepPrefabSetup : IPrefabSetup
|
||||
{
|
||||
public string GetTemplateName()
|
||||
{
|
||||
return "CreepTemplate";
|
||||
}
|
||||
|
||||
public void SetupPrefab(GameObject prefab, ScriptableObject data)
|
||||
{
|
||||
if (!(data is CreepData creepData))
|
||||
{
|
||||
Debug.LogWarning($"[CreepPrefabSetup] Expected CreepData, got {data.GetType().Name}");
|
||||
return;
|
||||
}
|
||||
|
||||
var creepDataComponent = prefab.GetComponent<CreepDataComponent>();
|
||||
if (creepDataComponent != null)
|
||||
{
|
||||
creepDataComponent.creepData = creepData;
|
||||
creepDataComponent.ApplyCreepData();
|
||||
}
|
||||
|
||||
var animationController = prefab.GetComponent<MonsterAnimationController>();
|
||||
if (animationController == null)
|
||||
{
|
||||
animationController = prefab.AddComponent<MonsterAnimationController>();
|
||||
Debug.Log($"[CreepPrefabSetup] Added MonsterAnimationController component");
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(creepData.modelPath))
|
||||
{
|
||||
RemoveOldModel(prefab);
|
||||
|
||||
if (creepData.modelPath.ToLower().EndsWith(".fbx"))
|
||||
{
|
||||
GameObject fbxModel = AssetDatabase.LoadAssetAtPath<GameObject>(creepData.modelPath);
|
||||
if (fbxModel != null)
|
||||
{
|
||||
GameObject fbxInstance = GameObject.Instantiate(fbxModel);
|
||||
fbxInstance.name = "Model";
|
||||
fbxInstance.transform.SetParent(prefab.transform, false);
|
||||
fbxInstance.transform.localPosition = Vector3.zero;
|
||||
fbxInstance.transform.localRotation = Quaternion.identity;
|
||||
fbxInstance.transform.localScale = Vector3.one;
|
||||
|
||||
Debug.Log($"[CreepPrefabSetup] Applied FBX model: {creepData.modelPath}");
|
||||
|
||||
Avatar modelAvatar = fbxModel.GetComponent<Animator>()?.avatar;
|
||||
|
||||
if (modelAvatar != null)
|
||||
{
|
||||
Animator prefabAnimator = prefab.GetComponent<Animator>();
|
||||
if (prefabAnimator != null)
|
||||
{
|
||||
prefabAnimator.avatar = modelAvatar;
|
||||
Debug.Log($"[CreepPrefabSetup] Applied Avatar: {modelAvatar.name}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[CreepPrefabSetup] Could not load FBX model: {creepData.modelPath}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var meshFilter = prefab.GetComponent<MeshFilter>();
|
||||
if (meshFilter != null)
|
||||
{
|
||||
Mesh mesh = AssetDatabase.LoadAssetAtPath<Mesh>(creepData.modelPath);
|
||||
if (mesh != null)
|
||||
{
|
||||
meshFilter.sharedMesh = mesh;
|
||||
Debug.Log($"[CreepPrefabSetup] Applied mesh: {creepData.modelPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[CreepPrefabSetup] Could not load mesh: {creepData.modelPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(creepData.animationControllerPath))
|
||||
{
|
||||
Animator animator = prefab.GetComponent<Animator>();
|
||||
if (animator != null)
|
||||
{
|
||||
RuntimeAnimatorController controller = AssetDatabase.LoadAssetAtPath<RuntimeAnimatorController>(creepData.animationControllerPath);
|
||||
if (controller != null)
|
||||
{
|
||||
animator.runtimeAnimatorController = controller;
|
||||
Debug.Log($"[CreepPrefabSetup] Applied Animator Controller: {creepData.animationControllerPath}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning($"[CreepPrefabSetup] Could not load Animator Controller: {creepData.animationControllerPath}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveOldModel(GameObject prefab)
|
||||
{
|
||||
Transform oldModel = prefab.transform.Find("Model");
|
||||
if (oldModel != null)
|
||||
{
|
||||
GameObject.DestroyImmediate(oldModel.gameObject);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Scripts/Editor/CreepPrefabSetup.cs.meta
Normal file
2
Assets/Scripts/Editor/CreepPrefabSetup.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74efb75ea8ccbe54a9c86d0fd503f85f
|
||||
@@ -86,6 +86,27 @@ namespace Northbound
|
||||
[Tooltip("배치 시도 최대 횟수")]
|
||||
[SerializeField] private int maxObstacleSpawnAttempts = 50;
|
||||
|
||||
[Header("Creep Camp Settings")]
|
||||
[Tooltip("크립 캠프 프리팹")]
|
||||
public GameObject creepCampPrefab;
|
||||
[Tooltip("크립 캠프 간 최소 거리")]
|
||||
[SerializeField] private float minDistanceBetweenCamps = 30f;
|
||||
[Tooltip("코어와의 최소 거리")]
|
||||
[SerializeField] private float minDistanceFromCoreCamps = 30f;
|
||||
[Tooltip("막사와의 최소 거리")]
|
||||
[SerializeField] private float minDistanceFromBarracksCamps = 30f;
|
||||
[Tooltip("초기 자원과의 최소 거리")]
|
||||
[SerializeField] private float minDistanceFromInitialResource = 30f;
|
||||
[Tooltip("추가 크립 캠프 개수")]
|
||||
[Range(0, 10)]
|
||||
[SerializeField] private int additionalCreepCampCount = 5;
|
||||
[Tooltip("크립 캠프 강도 기본값")]
|
||||
[SerializeField] private float baseCampStrength = 1f;
|
||||
[Tooltip("Z 위치에 따른 강도 증가율 (Z 100당)")]
|
||||
[SerializeField] private float strengthIncreasePerZ100 = 0.2f;
|
||||
[Tooltip("자원 보호 캠프 강도 보너스 (far camp보다 얼마나 강할지)")]
|
||||
[SerializeField] private float resourceCampStrengthBonus = 0.5f;
|
||||
|
||||
[Header("Generation Order")]
|
||||
[Tooltip("자원 먼저 생성 후 장애물 생성 (true) 또는 그 반대 (false)")]
|
||||
[SerializeField] private bool generateResourcesFirst = true;
|
||||
@@ -93,6 +114,8 @@ namespace Northbound
|
||||
private ResourceData[] _generatedResources;
|
||||
private float _globalProductionMultiplier = 1f;
|
||||
private List<Vector3> _spawnedPositions = new List<Vector3>();
|
||||
private List<Vector3> _creepCampPositions = new List<Vector3>();
|
||||
private List<GameObject> _creepPrefabs = new List<GameObject>();
|
||||
private Transform _objectsParent;
|
||||
|
||||
[System.Serializable]
|
||||
@@ -177,6 +200,7 @@ namespace Northbound
|
||||
}
|
||||
|
||||
_spawnedPositions.Clear();
|
||||
_creepCampPositions.Clear();
|
||||
|
||||
if (generateResourcesFirst)
|
||||
{
|
||||
@@ -189,6 +213,8 @@ namespace Northbound
|
||||
GenerateResources();
|
||||
}
|
||||
|
||||
GenerateCreepCamps();
|
||||
|
||||
Debug.Log($"[MapGenerator] Map generation complete!");
|
||||
}
|
||||
|
||||
@@ -658,6 +684,241 @@ namespace Northbound
|
||||
|
||||
#endregion
|
||||
|
||||
#region Creep Camp Generation
|
||||
|
||||
private void GenerateCreepCamps()
|
||||
{
|
||||
if (creepCampPrefab == null)
|
||||
{
|
||||
Debug.LogWarning("[MapGenerator] Creep camp prefab not assigned, skipping creep camp generation.");
|
||||
return;
|
||||
}
|
||||
|
||||
LoadCreepPrefabs();
|
||||
|
||||
if (_creepPrefabs.Count == 0)
|
||||
{
|
||||
Debug.LogWarning("[MapGenerator] No creep prefabs found in Assets/Prefabs/Creep/, skipping creep camp generation.");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Log($"[MapGenerator] Starting creep camp generation. Resources: {_generatedResources.Length}, Additional camps: {additionalCreepCampCount}");
|
||||
|
||||
int totalCampsSpawned = 0;
|
||||
|
||||
for (int i = 0; i < _generatedResources.Length; i++)
|
||||
{
|
||||
Vector3 campPosition = FindValidCampPositionForResource(_generatedResources[i].position);
|
||||
if (campPosition != Vector3.zero)
|
||||
{
|
||||
float strength = CalculateCampStrength(_generatedResources[i].position.y, isResourceCamp: true);
|
||||
Debug.Log($"[MapGenerator] Spawning resource camp {i + 1} for resource at {_generatedResources[i].position}, position: {campPosition}, strength: {strength:F2} (with bonus)");
|
||||
SpawnCreepCamp(campPosition, strength);
|
||||
_creepCampPositions.Add(campPosition);
|
||||
totalCampsSpawned++;
|
||||
}
|
||||
}
|
||||
|
||||
int additionalSpawned = 0;
|
||||
for (int i = 0; i < additionalCreepCampCount; i++)
|
||||
{
|
||||
Vector3 campPosition = FindValidCampPosition();
|
||||
if (campPosition != Vector3.zero)
|
||||
{
|
||||
float strength = CalculateCampStrength(campPosition.z, isResourceCamp: false);
|
||||
Debug.Log($"[MapGenerator] Spawning additional camp {i + 1}, position: {campPosition}, strength: {strength:F2}");
|
||||
SpawnCreepCamp(campPosition, strength);
|
||||
_creepCampPositions.Add(campPosition);
|
||||
additionalSpawned++;
|
||||
}
|
||||
}
|
||||
|
||||
Debug.Log($"[MapGenerator] Spawned {totalCampsSpawned} resource-based camps and {additionalSpawned} additional camps. Total: {totalCampsSpawned + additionalSpawned}");
|
||||
}
|
||||
|
||||
private void LoadCreepPrefabs()
|
||||
{
|
||||
Debug.Log($"[MapGenerator] LoadCreepPrefabs called, current count: {_creepPrefabs.Count}");
|
||||
|
||||
if (_creepPrefabs.Count > 0)
|
||||
{
|
||||
Debug.Log($"[MapGenerator] Skipping load, already have {_creepPrefabs.Count} prefabs");
|
||||
return;
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string[] prefabGuids = UnityEditor.AssetDatabase.FindAssets("t:Prefab", new[] { "Assets/Prefabs/Creep" });
|
||||
Debug.Log($"[MapGenerator] Found {prefabGuids.Length} prefabs in Assets/Prefabs/Creep/");
|
||||
_creepPrefabs.Clear();
|
||||
|
||||
foreach (string guid in prefabGuids)
|
||||
{
|
||||
string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(guid);
|
||||
GameObject prefab = UnityEditor.AssetDatabase.LoadAssetAtPath<GameObject>(assetPath);
|
||||
|
||||
if (prefab != null && prefab.GetComponent<CreepDataComponent>() != null)
|
||||
{
|
||||
_creepPrefabs.Add(prefab);
|
||||
Debug.Log($"[MapGenerator] Added creep prefab: {prefab.name}");
|
||||
}
|
||||
}
|
||||
#else
|
||||
Debug.LogWarning("[MapGenerator] Creep prefabs not loaded in build. Please assign creep prefabs manually in Inspector.");
|
||||
#endif
|
||||
|
||||
if (_creepPrefabs.Count > 0)
|
||||
{
|
||||
Debug.Log($"[MapGenerator] Loaded {_creepPrefabs.Count} creep prefabs from Assets/Prefabs/Creep/");
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("[MapGenerator] No creep prefabs loaded!");
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 FindValidCampPosition()
|
||||
{
|
||||
int maxAttempts = 200;
|
||||
|
||||
for (int attempt = 0; attempt < maxAttempts; attempt++)
|
||||
{
|
||||
Vector3 candidatePosition = GetRandomPositionInPlayableArea();
|
||||
|
||||
if (IsValidCreepCampPosition(candidatePosition))
|
||||
{
|
||||
return candidatePosition;
|
||||
}
|
||||
}
|
||||
|
||||
return Vector3.zero;
|
||||
}
|
||||
|
||||
private bool IsValidCreepCampPosition(Vector3 position)
|
||||
{
|
||||
if (Vector2.Distance(new Vector2(position.x, position.z), _corePosition) < minDistanceFromCoreCamps)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(new Vector2(position.x, position.z), _barracksPosition) < minDistanceFromBarracksCamps)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(new Vector2(position.x, position.z), _initialResourcePosition) < minDistanceFromInitialResource)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (var campPos in _creepCampPositions)
|
||||
{
|
||||
if (Vector3.Distance(position, campPos) < minDistanceBetweenCamps)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (position.x < -playableAreaWidth / 2f || position.x > playableAreaWidth / 2f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (position.z < startZ || position.z > endZ)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private Vector3 FindValidCampPositionForResource(Vector2 resourcePosition)
|
||||
{
|
||||
Vector3 bestPosition = Vector3.zero;
|
||||
float bestDistance = float.MaxValue;
|
||||
int maxAttempts = 50;
|
||||
|
||||
Debug.Log($"[MapGenerator] FindValidCampPositionForResource: Resource at {resourcePosition}, bounds: X:[{-playableAreaWidth/2f}, {playableAreaWidth/2f}], Z:[{startZ}, {endZ}]");
|
||||
|
||||
for (int attempt = 0; attempt < maxAttempts; attempt++)
|
||||
{
|
||||
float angle = Random.Range(0f, 360f);
|
||||
float distance = Random.Range(8f, 20f);
|
||||
Vector3 candidatePosition = new Vector3(
|
||||
resourcePosition.x + Mathf.Cos(angle * Mathf.Deg2Rad) * distance,
|
||||
1f,
|
||||
resourcePosition.y + Mathf.Sin(angle * Mathf.Deg2Rad) * distance
|
||||
);
|
||||
|
||||
if (IsValidCreepCampPosition(candidatePosition))
|
||||
{
|
||||
if (Vector3.Distance(candidatePosition, new Vector3(resourcePosition.x, 0, resourcePosition.y)) < bestDistance)
|
||||
{
|
||||
bestDistance = Vector3.Distance(candidatePosition, new Vector3(resourcePosition.x, 0, resourcePosition.y));
|
||||
bestPosition = candidatePosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bestPosition == Vector3.zero)
|
||||
{
|
||||
Debug.LogWarning($"[MapGenerator] Failed to find camp position for resource at {resourcePosition} after {maxAttempts} attempts");
|
||||
}
|
||||
|
||||
return bestPosition;
|
||||
}
|
||||
|
||||
private float CalculateCampStrength(float zPosition, bool isResourceCamp = false)
|
||||
{
|
||||
float normalizedZ = (zPosition - startZ) / (endZ - startZ);
|
||||
float strengthMultiplier = baseCampStrength + (normalizedZ * (strengthIncreasePerZ100 * (endZ - startZ) / 100f));
|
||||
|
||||
if (isResourceCamp)
|
||||
{
|
||||
strengthMultiplier *= resourceCampStrengthBonus;
|
||||
}
|
||||
|
||||
return strengthMultiplier;
|
||||
}
|
||||
|
||||
private void SpawnCreepCamp(Vector3 position, float strength)
|
||||
{
|
||||
GameObject campObj = Instantiate(creepCampPrefab, position, Quaternion.identity);
|
||||
|
||||
CreepCamp creepCamp = campObj.GetComponent<CreepCamp>();
|
||||
if (creepCamp == null)
|
||||
{
|
||||
Debug.LogError($"[MapGenerator] Creep camp prefab doesn't have CreepCamp component!");
|
||||
Destroy(campObj);
|
||||
return;
|
||||
}
|
||||
|
||||
creepCamp.InitializeCamp(position.z, strength);
|
||||
creepCamp.SetCreepPrefabs(_creepPrefabs);
|
||||
Debug.Log($"[MapGenerator] Camp initialized with {_creepPrefabs.Count} creep prefabs");
|
||||
|
||||
NetworkObject networkObj = campObj.GetComponent<NetworkObject>();
|
||||
if (networkObj == null)
|
||||
{
|
||||
networkObj = campObj.AddComponent<NetworkObject>();
|
||||
}
|
||||
|
||||
networkObj.Spawn();
|
||||
|
||||
if (groupUnderParent && _objectsParent != null)
|
||||
{
|
||||
campObj.transform.SetParent(_objectsParent);
|
||||
}
|
||||
|
||||
if (campObj.GetComponent<FogOfWarVisibility>() == null)
|
||||
{
|
||||
var visibility = campObj.AddComponent<FogOfWarVisibility>();
|
||||
visibility.showInExploredAreas = false;
|
||||
visibility.updateInterval = 0.2f;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
|
||||
public float GetTotalProduction()
|
||||
@@ -758,6 +1019,15 @@ namespace Northbound
|
||||
Gizmos.color = Color.blue;
|
||||
Gizmos.DrawWireSphere(new Vector3(_initialResourcePosition.x, 0, _initialResourcePosition.y), 5f);
|
||||
}
|
||||
|
||||
if (creepCampPrefab != null)
|
||||
{
|
||||
Gizmos.color = Color.red;
|
||||
foreach (var campPos in _creepCampPositions)
|
||||
{
|
||||
Gizmos.DrawWireSphere(campPos, 8f);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user