이젠 게임보다 아트가 재밌어진다...
이제부턴 주제를 따로 정하지 않고 그날 그날 작업한 내용 중 헤맸던 부분들만 작성해야겠다.
심화주차 팀 프로젝트 - 길건너 친구들 풍 맵 생성기
MapGenerator
완성되어야 하는 모습이다.
맵 생성에는 다음과 같은 조건을 따른다.
정말 복잡하지만 세 줄 요약하면 다음과 같다
- 잔디는 밝은색, 탁한색 페어로 되어있고 순환 가능해 같은 종류도 생성 가능하다.
- 도로는 차선 표시가 있는 연속도로는 반드시 처음과 끝이 있다.(중간도 있어 중복이 된다.)
- 1차선 싱글 도로는 연속과 순환이 불가능해 반드시 다른 플랫폼이 와야한다.
해당 조건을 따르기 위해서 다음 세가지 클래스들을 생성하였다.
public class PlatformBase : MonoBehaviour
public class ContinuousPlatform : PlatformBase
public class SinglePlatform : PlatformBase
Continuous와 PlatformBase가 핵심인데 이 둘의 내부를 보자.
public enum PlatformType
{
Land_01,
LoadPair_01,
LoadSingle,
}
public class PlatformBase : MonoBehaviour
{
public PlatformType platformType;
[field: SerializeField] public string Tag { get; private set; }
public virtual void Init() { }
}
이번 로직에선 특히 string이 많이 활용되는데 그 이유는 현재 프로젝트의 PoolManager의 보관,생성 로직이
오브젝트의 Tag(string)으로 이루어지기 때문이다.
PlatformBase 는 enum 값을 나중에 생성시에 쓰이는 메서드에서 string으로 변환해 매개변수로 활용한다.
Tag 프로퍼티는 실제 오브젝트의 Pool에서 쓰이는 Tag이다.
public class ContinuousPlatform : PlatformBase
{
[field: SerializeField] public bool IsEssential { get; private set; }
[field: SerializeField] public bool IsCyclable { get; private set; }
[field: SerializeField] public bool IsMid { get; private set; }
[field: SerializeField] public bool IsLast { get; private set; }
[field: SerializeField] public string NextPair { get; private set; }
}
ContinuousPlatform 에서 거의 모든 분기를 관리한다.
조금 복잡하다보니 bool 값이 많아졌다.
IsEssential : 반드시 나의 플랫폼 다음에 나와 페어인 플랫폼이 와야하는 경우.
IsCyclable : 같은 종류가 또 나올 때, 순환이 가능하지 않으면 나와 동류는 생성하지 않게 하기 위함.
IsMid : 연속의 중간이라 중복해서 나올 수 있는지 판단하기 위함.
IsLast : 필수는 아니지만 연속이고, Last가 아니라면 다음이 올 수 있게하기 위함.
NextPair : 나의 다음 플랫폼 오브젝트의 Tag
맵 생성기에선 생선된 플랫폼들의 정보를 다음과 같이 저장한다.
public class PlatformGenerator : MonoBehaviour
{
private Queue<PlatformBase> _platformsQueue = new Queue<PlatformBase>();
private PlatformBase _latestPlatform;
}
가장 마지막에 생성된 플랫폼을 PlatformBase로 저장하는데, 다음 플랫폼 생성시에 다음과 같이 쓰인다.
private void GeneratePlatform()
{
if (!IsEssentialPlatform())
GenerateRandomPlatform();
}
플랫폼 생성시, 지금 생성되어야 하는 플랫폼이 마지막 플랫폼과 연관있는 플랫폼인지 확인하는
IsEssentialPlatform 메서드를 호출해 여부를 확인한다.
// 마지막 플랫폼이 반드시 자신의 페어가 와야하는 플랫폼인지 검사
private bool IsEssentialPlatform()
{
ContinuousPlatform continuousPlatform = GetChildPlatform<ContinuousPlatform>();
// 자신의 페어가 와야하면 생성 후 true 반환
if (continuousPlatform != null && continuousPlatform.IsEssential)
{
// 페어가 오긴 해야하는데 중간꺼라 중복이 가능하면 확률적
if (continuousPlatform.IsMid)
{
string random = Random.Range(0, 10) < 5 ? continuousPlatform.NextPair : continuousPlatform.Tag;
GenerateEssentialPlatform(random);
return true;
}
// 중간이 없는 1, 2 페어면 바로 다음 플랫폼
GenerateEssentialPlatform(continuousPlatform.NextPair);
return true;
}
return false;
}
IsEssentialPlatform으로 필수 플랫폼이 필요하면
GenerateEssentialPlatform 메서드 호출로 필수 플랫폼을 생성한다.
private void GenerateEssentialPlatform(string platformType)
{
DeployPlatform(platformType);
}
만약 false가 나와 랜덤한 플랫폼을 생성해야 하면 GenerateRandomPlatform 메서드를 호출한다.
public void GenerateRandomPlatform()
{
string platformType = GetRandomTypeName();
DeployPlatform(platformType);
}
둘 다 마찬가지로 DeployPlatform 메서드를 호출하지만
랜덤의 경우 GetRandomTypeName 메서드로 string을 할당 받는다.
private string GetRandomTypeName()
{
// Enum으로 각 플랫폼의 1번 태그를 불러와 랜덤하게 지정
string randType = _platformTypes[Random.Range(0, _platformTypes.Length)];
if (_latestPlatform == null)
return randType;
ContinuousPlatform continuousPlatform = GetChildPlatform<ContinuousPlatform>();
if (continuousPlatform != null)
{
// 마지막 플랫폼이 1번 플랫폼이면, 연속 플랫폼인지 확인 후 마지막이 아니면 다음 플랫폼 태그를 뽑는다.
if (_latestPlatform.Tag == randType && continuousPlatform.IsLast == false)
return continuousPlatform.NextPair;
// 마지막 플랫폼의 타입이 생성될 타입과 같고, 순환이 가능하면 바로 생성한다.
if (_latestPlatform.platformType.ToString() == randType && continuousPlatform.IsCyclable)
return randType;
}
// 순환이 안 되거나, 같은 종류의 플랫폼이면 다시 뽑는다.
if (_latestPlatform.platformType == CheckNextPlatformType(randType))
return GetRandomTypeName();
// 그냥 다른거
return randType;
}
위 코드 중 주석으로 설명이 안 된 메서드 두 가지는 다음과 같다.
private PlatformType CheckNextPlatformType(string platformType)
{
PlatformBase platform = ObjectPoolManager.PeekObject(platformType).GetComponent<PlatformBase>();
return platform.platformType;
}
CheckNextPlatformType 메서드는 다음 플랫폼이 알기위해 랜덤으로 뽑은 태그를
PoolManager를 활용해 다음으로 올 Platform을 Peek을 통해 잠깐 꺼내보기만한다.
반환하는 타입은 Enum 이다.
// Continuous 또는 Single Platform을 제너릭을 활용해 반환
private T GetChildPlatform<T>() where T : PlatformBase
{
if (_latestPlatform == null || _latestPlatform.TryGetComponent(out T continuous) == false)
return null;
return continuous;
}
GetChildPlatform 메서드는 마지막 플랫폼이 연속인지 아닌지 알기위해
실제 오브젝트에서 Continuous 또는 Single 인지 제너릭을 통해 찾아온다.
해당 메서드 중
private string GetRandomTypeName()
private bool IsEssentialPlatform()
위 두 메서드를 활용해 처음 이미지를 통해 보였던 조건들을 만족시킨다.
그렇게하면 결과는 다음과 같다.
1차선 도로는 한 줄만 존재.
2차선 이상 도로는 처음과 끝이 존재 단, 그 다음 플랫폼은 도로가 올 수 없음.
잔디는 다음 페어가 필수가 아니지만, 연속+순환이 가능하기 때문에 한줄 또는 그 이상이 가능
'스파르타 내배캠' 카테고리의 다른 글
스파르타 내배캠 Unity 3기 - 45 (0) | 2024.03.05 |
---|---|
스파르타 내배캠 Unity 3기 - 44 (4) | 2024.03.05 |
스파르타 내배캠 Unity 3기 42일차 (0) | 2024.02.23 |
스파르타 내배캠 Unity 3기 41일차 (0) | 2024.02.23 |
스파르타 내배캠 Unity 3기 40일차 (0) | 2024.02.19 |