OldBlueWater/BlueWater/Assets/02.Scripts/Ai/Unit/UnitManager.cs

186 lines
6.5 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
// ReSharper disable once CheckNamespace
namespace BlueWaterProject
{
[Serializable]
public class UnitMatrix
{
public int soldiers; // 부대 안의 병사 수
public int rows; // 배치될 행의 갯수
public int columns; // 배치될 열의 갯수
//public int centerNum; // 부대의 중심 번호
public UnitMatrix(int soldiers, int rows, int columns)
{
this.soldiers = soldiers;
this.rows = rows;
this.columns = columns;
}
}
public class UnitManager : Singleton<UnitManager>
{
#region Property and variable
[Tooltip("바닥 레이어")]
[field: SerializeField] public LayerMask GroundLayer { get; private set; }
[Tooltip("바닥과의 최대 허용 거리")]
[field: SerializeField] public float MaxGroundDistance { get; private set; } = 0.5f;
[Tooltip("병력 간의 간격")]
[field: SerializeField] public float SoldierSpacing { get; private set; } = 0.5f;
[Tooltip("부대 배치 행렬")]
[field: SerializeField] public List<UnitMatrix> UnitMatrices { get; private set; } = new(9);
[Tooltip("병력들의 프리팹 리스트(순서 중요)")]
[SerializeField] private List<GameObject> soliderPrefabList = new(10);
#endregion
#region Unity built-in function
protected override void Awake()
{
base.Awake();
GroundLayer = LayerMask.GetMask("Ground");
InitUnitMatrices();
}
private void Reset()
{
GroundLayer = LayerMask.GetMask("Ground");
MaxGroundDistance = 0.5f;
SoldierSpacing = 0.5f;
soliderPrefabList = new List<GameObject>(10); // TODO : 프리팹 자동 리셋화 필요
InitUnitMatrices();
}
#endregion
#region Custom function
/// <summary>
/// 부대 배치 행렬 초기화 함수
/// </summary>
[ContextMenu("InitUnitMatrices")]
public void InitUnitMatrices()
{
UnitMatrices = new List<UnitMatrix>(9)
{
new(1, 1, 1),
new(2, 1, 2),
new(3, 1, 3),
new(4, 2, 2),
new(6, 2, 3),
new(8, 2, 4),
new(9, 3, 3),
new(12, 3, 4),
new(16, 4, 4),
};
}
/// <summary>
/// 부대 생성 함수
/// </summary>
public void CreateUnit(UnitController unitController)
{
DestroyDeployedSoldiers(unitController);
var baseName = soliderPrefabList[(int)unitController.unit.unitType - 1].name;
if (string.IsNullOrEmpty(unitController.unit.unitName))
{
const int maxIterations = 100;
var namingNum = 0;
while (namingNum < maxIterations)
{
var newUnitName = $"{baseName}_Unit_{namingNum + 1:00}";
var checkGameObject = GameObject.Find(newUnitName);
if (checkGameObject && checkGameObject != unitController.gameObject)
{
namingNum++;
}
else
{
unitController.gameObject.name = newUnitName;
break;
}
}
}
else
{
unitController.gameObject.name = unitController.unit.unitName;
}
unitController.gameObject.layer = LayerMask.NameToLayer("Unit");
unitController.unit.soldierList = new List<AiController>(unitController.unit.soliderCount);
var matrix = UnitMatrices.Find(um => um.soldiers == unitController.unit.soliderCount);
if (matrix == null)
{
Debug.LogError("사용할 수 없는 병력의 숫자입니다. UnitManager의 UnitMatrices를 확인해주세요.");
return;
}
for (var i = 0; i < unitController.unit.soliderCount; i++)
{
var row = i / matrix.columns;
var column = i % matrix.columns;
var xOffset = (column - (matrix.columns - 1) / 2.0f) * SoldierSpacing;
var zOffset = (row - (matrix.rows - 1) / 2.0f) * SoldierSpacing;
var spawnPosition = unitController.transform.position + new Vector3(xOffset, 0, zOffset);
var ray = new Ray(spawnPosition, Vector3.down);
if (Physics.Raycast(ray, out var hit, MaxGroundDistance, GroundLayer))
{
spawnPosition.y = hit.point.y;
}
else
{
Debug.LogError("병력이 땅과 맞닿아 있지 않아 배치할 수 없는 위치입니다.");
DestroyDeployedSoldiers(unitController);
return;
}
var soldierObject = Instantiate(soliderPrefabList[(int)unitController.unit.unitType - 1], spawnPosition,
Quaternion.identity, unitController.transform).GetComponent<AiController>();
soldierObject.transform.localRotation = Quaternion.identity;
var newSoldierName = $"{baseName}_{i + 1:00}";
soldierObject.name = newSoldierName;
unitController.unit.soldierList.Add(soldierObject);
}
}
/// <summary>
/// 기존에 생성된 부대 병력들이 있으면 확인해서 삭제해주는 함수
/// </summary>
private void DestroyDeployedSoldiers(UnitController unitController)
{
if (unitController.transform.childCount <= 0) return;
for (var i = unitController.transform.childCount - 1; i >= 0; i--)
{
if (Application.isPlaying)
{
Destroy(unitController.transform.GetChild(i).gameObject);
}
else
{
DestroyImmediate(unitController.transform.GetChild(i).gameObject);
}
}
}
#endregion
}
}