484 lines
18 KiB
C#
484 lines
18 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEditor.Animations;
|
|
using UnityEngine;
|
|
using UnityEngine.Serialization;
|
|
|
|
// ReSharper disable once CheckNamespace
|
|
namespace BlueWaterProject
|
|
{
|
|
[Serializable]
|
|
public class UnitMatrix
|
|
{
|
|
public int units; // 부대 안의 병사 수
|
|
public int rows; // 배치될 행의 갯수
|
|
public int columns; // 배치될 열의 갯수
|
|
//public int centerNum; // 부대의 중심 번호
|
|
|
|
public UnitMatrix(int units, int rows, int columns)
|
|
{
|
|
this.units = units;
|
|
this.rows = rows;
|
|
this.columns = columns;
|
|
}
|
|
}
|
|
|
|
public class UnitManager : Singleton<UnitManager>
|
|
{
|
|
#region Property and variable
|
|
|
|
[Tooltip("유닛 프리팹")]
|
|
[field: SerializeField] public GameObject UnitPrefab { get; private set; }
|
|
|
|
[Tooltip("캐릭터 기초 프리팹")]
|
|
[field: SerializeField] public GameObject BaseCharacterPrefab { get; private set; }
|
|
|
|
[Tooltip("화살 프리팹")]
|
|
[field: SerializeField] public GameObject ArrowPrefab { get; private set; }
|
|
|
|
[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 UnitSpacing { get; private set; } = 0.5f;
|
|
|
|
[Tooltip("부대 배치 행렬")]
|
|
[field: SerializeField] public List<UnitMatrix> UnitMatrices { get; private set; } = new(MATRICES_CAPACITY);
|
|
|
|
[FormerlySerializedAs("soldierPrefabList")]
|
|
[Tooltip("병력들의 프리팹 리스트(순서 중요)")]
|
|
[SerializeField] private List<GameObject> characterPrefabList = new(CHARACTER_PREFAB_CAPACITY);
|
|
|
|
[field: Tooltip("병력들의 애니메이터 컨트롤러 리스트")]
|
|
[field: SerializeField] public List<AnimatorController> AIAnimatorControllerList { get; private set; } = new(GlobalValue.AI_ANIMATOR_CAPACITY);
|
|
|
|
[Tooltip("플레이어가 가지고 있는 부대리스트")]
|
|
[SerializeField] private List<UnitController> playerUnitList = new(PLAYER_UNIT_CAPACITY);
|
|
|
|
public IReadOnlyList<UnitController> PlayerUnitList => playerUnitList;
|
|
|
|
private Transform playerUnits;
|
|
|
|
private const int MATRICES_CAPACITY = 9;
|
|
private const int CHARACTER_PREFAB_CAPACITY = 10;
|
|
private const int PLAYER_UNIT_CAPACITY = 50;
|
|
|
|
#endregion
|
|
|
|
#region Unity built-in function
|
|
|
|
protected override void OnAwake()
|
|
{
|
|
GroundLayer = LayerMask.GetMask("Ground");
|
|
InitUnitMatrices();
|
|
InitPlayerUnitList();
|
|
}
|
|
|
|
private void Reset()
|
|
{
|
|
GroundLayer = LayerMask.GetMask("Ground");
|
|
MaxGroundDistance = 0.5f;
|
|
UnitSpacing = 0.5f;
|
|
characterPrefabList = new List<GameObject>(CHARACTER_PREFAB_CAPACITY);
|
|
InitUnitMatrices();
|
|
InitCharacterPrefabList();
|
|
InitPlayerUnitList();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Custom function
|
|
|
|
/// <summary>
|
|
/// 부대 배치 행렬 초기화 함수
|
|
/// </summary>
|
|
[GUIColor(0, 1, 0)]
|
|
[ShowIf("@UnitMatrices.Count != MATRICES_CAPACITY")]
|
|
[Button("행렬 초기화")]
|
|
private void InitUnitMatrices()
|
|
{
|
|
UnitMatrices = new List<UnitMatrix>(MATRICES_CAPACITY)
|
|
{
|
|
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),
|
|
};
|
|
}
|
|
|
|
#if UNITY_EDITOR
|
|
/// <summary>
|
|
/// 프리팹 초기화 함수
|
|
/// </summary>
|
|
[GUIColor(0, 1, 0)]
|
|
[ShowIf("@BaseCharacterPrefab == null || UnitPrefab == null || ArrowPrefab == null")]
|
|
[Button("프리팹 초기화")]
|
|
private void InitCharacterPrefabList()
|
|
{
|
|
UnitPrefab = Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character", "Unit");
|
|
BaseCharacterPrefab = Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character", "BaseCharacter");
|
|
ArrowPrefab = Utils.LoadPrefabFromFolder("Assets/05.Prefabs", "Arrow_01");
|
|
|
|
characterPrefabList = new List<GameObject>(CHARACTER_PREFAB_CAPACITY)
|
|
{
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Enemy", "Archer_E"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Enemy", "SpearKnight_E"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Enemy", "Spearman_E"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Enemy", "SwordKnight_E"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Enemy", "Swordman_E"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Pirate", "Archer_P"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Pirate", "Axeman_P"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Pirate", "Spearman_P"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Pirate", "SwordKnight_P"),
|
|
Utils.LoadPrefabFromFolder("Assets/05.Prefabs/Character/Pirate", "Swordman_P")
|
|
};
|
|
}
|
|
#endif
|
|
|
|
/// <summary>
|
|
/// 플레이어가 가진 유닛 리스트 초기화
|
|
/// </summary>
|
|
[GUIColor(0, 1, 0)]
|
|
[Button("플레이어 유닛 가져오기")]
|
|
private void InitPlayerUnitList()
|
|
{
|
|
SetPlayerUnits();
|
|
|
|
playerUnitList = new List<UnitController>(PLAYER_UNIT_CAPACITY);
|
|
|
|
foreach (Transform item in playerUnits)
|
|
{
|
|
if (!item.gameObject.activeSelf) continue;
|
|
|
|
playerUnitList.Add(item.GetComponent<UnitController>());
|
|
}
|
|
}
|
|
|
|
private void SetPlayerUnits()
|
|
{
|
|
var playerUnitsObj = GameObject.Find("PlayerUnits");
|
|
if (playerUnitsObj)
|
|
{
|
|
playerUnits = playerUnitsObj.transform;
|
|
}
|
|
else
|
|
{
|
|
playerUnitsObj = new GameObject("PlayerUnits");
|
|
playerUnitsObj.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
|
|
playerUnits = playerUnitsObj.transform;
|
|
}
|
|
}
|
|
|
|
private void SetUnitName(UnitController unitController, string baseName)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 동적 생성용 부대 생성 함수
|
|
/// </summary>
|
|
public void CreateUnit(string cardIdx, AttackerType attackerType)
|
|
{
|
|
var card = DataManager.Inst.GetCardDictionaryKey(cardIdx);
|
|
var unit = DataManager.Inst.GetUnitDictionaryKey(card.UnitIdx);
|
|
var captainStat = DataManager.Inst.GetAiStatDictionaryKey(unit.CaptainStatIdx);
|
|
var sailorStat = DataManager.Inst.GetAiStatDictionaryKey(unit.SailorStatIdx);
|
|
|
|
SetPlayerUnits();
|
|
|
|
var newUnitController = Instantiate(UnitPrefab, Vector3.zero, Quaternion.identity, playerUnits).GetComponent<UnitController>();
|
|
newUnitController.unit = new Unit(unit);
|
|
|
|
DestroyDeployedUnits(newUnitController);
|
|
|
|
var baseName = newUnitController.unit.UnitType.ToString();
|
|
SetUnitName(newUnitController, baseName);
|
|
|
|
newUnitController.unit.UnitList = new List<AiController>(newUnitController.unit.SailorCount + 1);
|
|
|
|
var unitControllerTransform = newUnitController.transform;
|
|
var unitControllerRotation = unitControllerTransform.rotation;
|
|
unitControllerTransform.rotation = Quaternion.identity;
|
|
|
|
var gridSize = 0;
|
|
|
|
switch (newUnitController.unit.SailorCount)
|
|
{
|
|
case 0:
|
|
gridSize = 1;
|
|
break;
|
|
case <= 3:
|
|
gridSize = 2;
|
|
break;
|
|
case <= 8:
|
|
gridSize = 3;
|
|
break;
|
|
case <= 15:
|
|
gridSize = 4;
|
|
break;
|
|
default:
|
|
print("유닛의 병사 숫자 설정 에러");
|
|
break;
|
|
}
|
|
|
|
var heroPosition = (gridSize * gridSize) / 2;
|
|
|
|
for (var i = 0; i < gridSize; i++)
|
|
{
|
|
for (var j = 0; j < gridSize; j++)
|
|
{
|
|
var currentPos = i * gridSize + j;
|
|
|
|
if (currentPos > newUnitController.unit.SailorCount) break;
|
|
|
|
var xOffset = (i - (gridSize - 1) / 2.0f) * UnitSpacing;
|
|
var zOffset = (j - (gridSize - 1) / 2.0f) * UnitSpacing;
|
|
var spawnPosition = unitControllerTransform.position + new Vector3(xOffset, 0, zOffset);
|
|
|
|
var baseObj = Instantiate(BaseCharacterPrefab, spawnPosition,
|
|
Quaternion.identity, newUnitController.transform);
|
|
|
|
var newSoldierName = $"{baseName}_{currentPos + 1:00}";
|
|
|
|
baseObj.name = newSoldierName;
|
|
baseObj.gameObject.SetActive(false);
|
|
|
|
var aiController = GetAiController(baseObj, newUnitController.unit.UnitType, currentPos == heroPosition ? captainStat : sailorStat);
|
|
|
|
aiController.SetAttackerType(attackerType);
|
|
newUnitController.unit.UnitList.Add(aiController);
|
|
}
|
|
}
|
|
newUnitController.transform.rotation *= unitControllerRotation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 에디터용 부대 생성 함수
|
|
/// </summary>
|
|
public void CreateUnit(UnitController unitController)
|
|
{
|
|
var unit = DataManager.Inst.GetUnitSoKey(unitController.unit.Idx);
|
|
|
|
SetPlayerUnits();
|
|
|
|
unitController.unit = new Unit(unit);
|
|
|
|
var captainStat = DataManager.Inst.GetAiStatSoKey(unitController.unit.CaptainStatIdx);
|
|
var sailorStat = DataManager.Inst.GetAiStatSoKey(unitController.unit.SailorStatIdx);
|
|
|
|
DestroyDeployedUnits(unitController);
|
|
|
|
var baseName = unitController.unit.UnitType.ToString();
|
|
SetUnitName(unitController, baseName);
|
|
|
|
unitController.unit.UnitList = new List<AiController>(unitController.unit.SailorCount + 1);
|
|
|
|
var unitControllerTransform = unitController.transform;
|
|
var unitControllerRotation = unitControllerTransform.rotation;
|
|
unitControllerTransform.rotation = Quaternion.identity;
|
|
|
|
var gridSize = 0;
|
|
|
|
switch (unitController.unit.SailorCount)
|
|
{
|
|
case 0:
|
|
gridSize = 1;
|
|
break;
|
|
case <= 3:
|
|
gridSize = 2;
|
|
break;
|
|
case <= 8:
|
|
gridSize = 3;
|
|
break;
|
|
case <= 15:
|
|
gridSize = 4;
|
|
break;
|
|
default:
|
|
print("유닛의 병사 숫자 설정 에러");
|
|
break;
|
|
}
|
|
|
|
var heroPosition = (gridSize * gridSize) / 2;
|
|
|
|
for (var i = 0; i < gridSize; i++)
|
|
{
|
|
for (var j = 0; j < gridSize; j++)
|
|
{
|
|
var currentPos = i * gridSize + j;
|
|
|
|
if (currentPos > unitController.unit.SailorCount) break;
|
|
|
|
var xOffset = (i - (gridSize - 1) / 2.0f) * UnitSpacing;
|
|
var zOffset = (j - (gridSize - 1) / 2.0f) * UnitSpacing;
|
|
var spawnPosition = unitControllerTransform.position + new Vector3(xOffset, 0, zOffset);
|
|
|
|
var baseObj = Instantiate(BaseCharacterPrefab, spawnPosition,
|
|
Quaternion.identity, unitController.transform);
|
|
|
|
var newSoldierName = $"{baseName}_{currentPos + 1:00}";
|
|
|
|
baseObj.name = newSoldierName;
|
|
baseObj.gameObject.SetActive(false);
|
|
|
|
var aiController = GetAiController(baseObj, unitController.unit.UnitType, currentPos == heroPosition ? captainStat : sailorStat);
|
|
|
|
aiController.SetAttackerType(unitController.unit.AttackerType);
|
|
aiController.InitStartInEditor();
|
|
unitController.unit.UnitList.Add(aiController);
|
|
}
|
|
}
|
|
unitController.transform.rotation *= unitControllerRotation;
|
|
}
|
|
|
|
private AiController GetAiController(GameObject baseObj, GlobalValue.UnitType unitType, AiStat aiStat)
|
|
{
|
|
AiController temp = null;
|
|
switch (unitType)
|
|
{
|
|
case GlobalValue.UnitType.NONE:
|
|
break;
|
|
case GlobalValue.UnitType.ARCHER_E:
|
|
temp = baseObj.AddComponent<Archer>();
|
|
break;
|
|
case GlobalValue.UnitType.SPEAR_KNIGHT_E:
|
|
temp = baseObj.AddComponent<SpearKnight>();
|
|
break;
|
|
case GlobalValue.UnitType.SPEARMAN_E:
|
|
temp = baseObj.AddComponent<Spearman>();
|
|
break;
|
|
case GlobalValue.UnitType.SWORD_KNIGHT_E:
|
|
temp = baseObj.AddComponent<SwordKnight>();
|
|
break;
|
|
case GlobalValue.UnitType.SWORDMAN_E:
|
|
temp = baseObj.AddComponent<Swordman>();
|
|
break;
|
|
case GlobalValue.UnitType.ARCHER_P:
|
|
temp = baseObj.AddComponent<Archer>();
|
|
break;
|
|
case GlobalValue.UnitType.AXEMAN_P:
|
|
temp = baseObj.AddComponent<Axeman>();
|
|
break;
|
|
case GlobalValue.UnitType.SPEARMAN_P:
|
|
temp = baseObj.AddComponent<Spearman>();
|
|
break;
|
|
case GlobalValue.UnitType.SWORD_KNIGHT_P:
|
|
temp = baseObj.AddComponent<SwordKnight>();
|
|
break;
|
|
case GlobalValue.UnitType.SWORDMAN_P:
|
|
temp = baseObj.AddComponent<Swordman>();
|
|
break;
|
|
default:
|
|
throw new ArgumentOutOfRangeException(nameof(unitType), unitType, null);
|
|
}
|
|
|
|
if (temp == null) return null;
|
|
|
|
temp.AiStat = new AiStat(aiStat);
|
|
return temp;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 유닛 배치 함수
|
|
/// </summary>
|
|
public bool CanAssignUnit(UnitController unitController, Vector3 assignPos)
|
|
{
|
|
if (unitController.unit.UnitList.Count <= 0) return false;
|
|
|
|
unitController.transform.position = assignPos;
|
|
|
|
for (var i = 0; i < unitController.unit.SailorCount; i++)
|
|
{
|
|
var soldierPos = unitController.unit.UnitList[i].transform.position;
|
|
var ray = new Ray(soldierPos, Vector3.down);
|
|
if (Physics.Raycast(ray, out var hit, MaxGroundDistance, GroundLayer))
|
|
{
|
|
soldierPos.y = hit.point.y;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void AssignUnit(UnitController unitController, Vector3 assignPos)
|
|
{
|
|
unitController.transform.position = assignPos;
|
|
foreach (var item in unitController.unit.UnitList)
|
|
{
|
|
item.gameObject.SetActive(true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 기존에 생성된 부대 병력들이 있으면 확인해서 삭제해주는 함수
|
|
/// </summary>
|
|
private void DestroyDeployedUnits(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);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// playerUnitList 내의 속성
|
|
/// </summary>
|
|
public void RemovePlayerUnitListElement(UnitController unitController)
|
|
{
|
|
if (playerUnitList.Contains(unitController))
|
|
{
|
|
playerUnitList.Remove(unitController);
|
|
}
|
|
else
|
|
{
|
|
Debug.Log("제거하려는 속성이 리스트 내에 존재하지 않습니다.");
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |