
심화 주차 프로젝트 때 만든 게임은 냥코대전쟁을 레퍼런스로 한 디펜스 게임 이었다.

이 게임에서는 다양한 종류의 유닛이 나온다.
처음에는 아군 유닛을 생산하기 위해 다음과 같은 코드를 작성하였다.
public void GenUnit(int cost, UnitSO unitSO)
{
GameObject unit = GameManager.Instance.ObjectPool.SpawnFromPool(unitSO.unitName);
unit.transform.position = unitGeneratePos.position;
GameManager.Instance.coin -= cost;
}
SpawnFromPool은 오브젝트풀 큐에서 유닛을 생성하는 메서드이다.
각 유닛마다 프리팹화 하고, 오브젝트 풀에서 미리 생성한 다음, 해당하는 버튼을 누르면 해당하는 unitName을 가진 유닛이 생성되는 방식이었다.
그러나 개발이 진행되던 도중 다음과 같은 의문이 발생했다.
1. 애초에 유닛들의 차이는 크지 않고, 스프라이터와 애니메이터를 포함한 정보를 담은 SO만 다르다.
2. 그럼 하나의 유닛 오브젝트에 필요한 SO만 바꾸고, 스탯만 잘 초기화 해주면 여러 오브젝트를 미리 만들 필요가 없지 않은가?
실제로 적 유닛은 위의 방식처럼 생성 되었지만,(참고: https://jooglorystar98.tistory.com/46) 아군 유닛은 모든 프리팹을 미리 만들고, 그에 맞는 유닛을 생산하는 식이었다.
그래서 적 유닛과 유사한 방식으로 코드를 수정하고, 적 유닛과 아군 유닛이 같은 프리팹을 공유하지만, SO만 바꿈으로서, 적, 아군 여부와 어떤 유닛인지에 대해서도 다르게 구현할 수 있게 되었다.
최종 아군 유닛 생산 코드는 다음과 같았다.
public void GenUnit(int cost, UnitSO unitSO)
{
GameObject obj = GameManager.Instance.ObjectPool.SpawnFromPool("Unit");
Unit unit = obj.GetComponent<Unit>();
unit.unitStat.unitSO = unitSO;
unit.unitStat.SetLevel();
unit.unitStat.SetStatAsLevel();
unit.unitStat.SetUnitLayer();
unit.transform.position = unitGeneratePos.position;
int genPosYValue = Random.Range(-2, 3);
unit.GetComponentInChildren<SpriteRenderer>().sortingOrder -= genPosYValue;
genPosVariation = new Vector3(0f, genPosYValue * 0.1f, 0f);
unit.transform.position += genPosVariation;
cost = unitSO.generateCost;
GameManager.Instance.CoinManager.coinCount -= cost;
}
다만 개선점이 필요하다고 느낀 점은 적과 아군 유닛에 코드가 겹치는 부분이 생긴다는 점이었다.
IEnumerator RenderSpawn()
{
while (!GameManager.Instance.isGameEnd)
{
GameObject obj = GameManager.Instance.ObjectPool.SpawnFromPool("Unit");
Unit unit = obj.GetComponent<Unit>();
int value = Random.Range(0, enemyUnitSOs.Length);
unit.unitStat.unitSO = enemyUnitSOs[value];
unit.unitStat.SetLevel();
unit.unitStat.SetStatAsLevel();
unit.unitStat.SetUnitLayer();
// 스폰 Y축 설정
unit.transform.position = enemySpawnPos.position;
int genPosYValue = Random.Range(-2, 3);
unit.GetComponentInChildren<SpriteRenderer>().sortingOrder -= genPosYValue;
genPosVariation = new Vector3(0f, genPosYValue * 0.1f, 0f);
unit.transform.position += genPosVariation;
yield return enemySpawnInterval;
}
}
아군 유닛 생산은 Coin이 감소하고, 적 유닛 생산은 코루틴에서 진행되고, 랜덤 값에 따라 SO를 삽입한다는 는 점을 제외하면, 사실상 유사한 코드였다.
두 클래스 모두에서 사용할 수 있는 클래스에서 유닛을 생산할 수 있는 메서드를 만들고, 아군과 적 생산 메서드에서 각각 호출한다면, 유지보수에도 도움이 될 수 있을 것이라 생각됐다.
그래서 GameManager에서 참조하는 GenerateUnit 클래스에 다음과 같은 메서드들을 추가했다.
public void SpawnUnit(UnitSO unitSO, Transform spawnPos)
{
GameObject obj = GameManager.Instance.ObjectPool.SpawnFromPool("Unit");
Unit unit = SetUnitObject(obj);
SetUnitStat(unit, unitSO);
SetUnitPosition(unit, spawnPos);
}
private Unit SetUnitObject(GameObject obj)
{
Unit unit = obj.GetComponent<Unit>();
return unit;
}
private void SetUnitStat(Unit unit, UnitSO unitSO)
{
unit.unitStat.unitSO = unitSO;
unit.unitStat.SetLevel();
unit.unitStat.SetStatAsLevel();
unit.unitStat.SetUnitLayer();
}
private void SetUnitPosition(Unit unit, Transform spawnPos)
{
unit.transform.position = spawnPos.position;
int genPosYValue = Random.Range(-2, 3);
unit.GetComponentInChildren<SpriteRenderer>().sortingOrder -= genPosYValue;
Vector3 genPosVariation = new Vector3(0f, genPosYValue * 0.1f, 0f);
unit.transform.position += genPosVariation;
}
SetUnitObject는 게임오브젝트를 매개변수로 받고, 그걸 Unit 형태로 반환하는 메서드이다.
SetUnitStat은 Unit과 UnitSO를 매개변수로 받고 스탯을 초기화해주는 메서드이다.
SetUnitPosition은 유닛이 생성될 때의 위치를 정해주는 메서드이다.
그리고 SpawnUnit은 UnitSO와 소환될 위치 객체의 Trasform을 받으면
오브젝트 풀에서 생성하는 것 부터, 위 설명한 메서드들을 모두 작동시킬 수 있도록 만들었다.
결과적으로 두 코드는 다음과 같이 변하였다.
// 아군 유닛 생성
public void GenUnit(int cost, UnitSO unitSO)
{
SpawnUnit(unitSO, unitGeneratePos);
cost = unitSO.generateCost;
GameManager.Instance.CoinManager.coinCount -= cost;
}
// 적 유닛 생성
IEnumerator RenderSpawn()
{
while (!GameManager.Instance.isGameEnd)
{
int value = Random.Range(0, enemyUnitSOs.Length);
GameManager.Instance.GenerateUnit.SpawnUnit(enemyUnitSOs[value], enemySpawnPos);
yield return enemySpawnInterval;
}
}
아군 유닛 생성은 GenerateUnit에 바로 있었기에, 메서드를 바로 호출 했지만,
적 유닛 생성은 다른 스크립트에 있었기 때문에 위 처럼 게임 메니저 인스턴스를 통해 호출하게 되었다.
결과적으로 두 코드의 겹치던 부분을 통합하고, 재사용이 용이하게 만들 수 있었다.
'내일배움캠프 TIL' 카테고리의 다른 글
| 내일배움캠프 49일차 TIL "타일맵 저장의 아이디어" (0) | 2024.11.26 |
|---|---|
| 내일배움캠프 48일차 TIL "Summary 기능" (0) | 2024.11.25 |
| 내일배움캠프 46일차 TIL "유닛 스탯 적용 트러블 슈팅" (0) | 2024.11.21 |
| 내일배움캠프 45일차 TIL "유니티의 생명주기 함수" (0) | 2024.11.20 |
| 내일배움캠프 44일차 TIL "유닛에 상태 패턴 적용" (0) | 2024.11.19 |