본문으로 바로가기

[Wiggle Quest - 3] 아이템 스폰 시스템

category Project/Wiggle Quest 2024. 3. 6. 17:30

지난 글에서는 플레이어 성장에 대한 파트를 개발했다. 

1. 지렁이가 땅에 랜덤으로 떨어지는 먹이를 먹으면 몸이 늘어나고(레벨증가) , 장애물에 부딪히면 몸이 줄어든다.(레벨감소)
2. 지렁이가 땅에 랜덤으로 떨어지는 골드를 먹으면 '상점'에서 여러가지 버프와, 갑옷 구매가 가능하다.
3. 지렁이의 몸이 특정 레벨까지 오르면 게임 클리어
4. 지렁이의 몸이 모두 사라지면 게임 오버

이번엔  이 중 굵은 글씨로 표시해놓은 "필드의 아이템 스폰 시스템"를 개발해보려 한다

 

 

- 우선 각각의 아이템 서로 겹치지 않고 생성되게 하는 것을 목표로, 어떤 방법이 있을까 고민하다가 아래 그림처럼 물결 모양의 그래프대로 생성한다면 어떨까라는 생각이 들었다.

물결 모양의 그래프

그리고 그후 내가 원하는 것과 아주 비슷한 모양인 사인 그래프가 떠올랐다.

사인 그래프

사인 그래프를 원하는 크기만큼 조절하고, x값이 겹치지 않게 만들면 서로 겹치지않게 아이템들을 스폰할 수 있을 것이다!

 

그렇다면 사인 그래프를 내가 원하는 사이즈에 맞게 조절하여 생성하는 기능을 구현해보자.

x좌표

offset : 그래프 여러개가 겹칠때 오브젝트가 겹쳐서 생성되는 것을 막기 위해 띄우는 용도

unit : x좌표 단위

를 활용하여 총 아이템 개수에 따라 위치를 지정해준다.

 

y(코드에서는 Z로 표기)좌표는 위에서 구한 x좌표를 활용하여 크기 * Sin(드롭간격배수 * x) 로 구한다. 드롭간격배수가 높아지면 주기를 짧게 만들어 그래프 간격이 좁아진다. 같은 오브젝트 개수여도 이 프로퍼티로 랜덤한 위치를 구현할 수 있다!

 

<예시그림>

float numX = offset + unit * i;
float numZ = (thisSize.z / 2 - offset *2) * Mathf.Sin(cycleMulti * numX) + (thisSize.z / 2);

위에서 구해준 X,Y(Z)값을 원점으로부터 거리에 각각 더해주면 ㅡ 일정 간격으로 겹치지않게 아이템를 생성하는 기능이 구현되었다! 

 

- 세종류 ( 함정, 먹이, 골드 ) 의 아이템을 랜덤하게 반환하는 기능

아이템 종류마다 생성되는 개수가 다르기 때문에 새로운 리스트를 만든 후, 아직 만들지 않은 아이템의 개수가 남아있다면, 리스트에 각각의 프리팹을 추가해, 랜덤으로 뽑는 방식으로 구현해보았다.

        public GameObject RandomItemCreate()
        {
            List<GameObject> prefabList = new List<GameObject>() { firePrefab, goldPrefab, feedPrefab };
            if (numFire == 0)
                prefabList.Remove(firePrefab);
            if (numGold == 0)
                prefabList.Remove(goldPrefab);
            if (numFeed == 0)
                prefabList.Remove(feedPrefab);
                
            if (prefabList.Count != 0)
            {
                int rand = Random.Range(0, prefabList.Count);
                if (prefabList[rand] == firePrefab)
                {
                    numFire--;
                    return Instantiate(prefabList[rand], groupTrap.transform);
                }
                else
                {
                    if (prefabList[rand] == goldPrefab)
                        numGold--;
                    else if (prefabList[rand] == feedPrefab)
                        numFeed--;

                    return Instantiate(prefabList[rand], groupDrop.transform);
                }
            }

 

 

<지금까지 구현한 기능을 가져와 사용하는 메서드>

리스트에 추가해 삭제기능까지 같이 관리하도록 했다.

        [SerializeField] private List<GameObject> FieldItemList = new List<GameObject>();

        public IEnumerator CreateItems()
        {
        	...
            
            for (int i = 0; i < total; i++)
            {
                item = RandomItemCreate();
                FieldItemList.Add(item);

                Vector3 thisPos = this.transform.position - (thisSize / 2);

                float numX = offset + unit * i;
                float numZ = (thisSize.z / 2 - offset *2) * Mathf.Sin(cycleMulti * numX) + (thisSize.z / 2);

                FieldItemList[i].transform.position = new Vector3(thisPos.x + numX, 0f, thisPos.z + numZ);
            }
            
            yield return null;
        }

 

- 스폰 타일을 생성

지금까지 개발한 스폰 시스템은 사실 전체가 아닌 하나의 타일 안에서 이루어져야만 한다. 각각의 타일로 구역을 나누어 각 구역마다 아이템 생성과 삭제가 가능해야지만 첫번째로 개발했던 '끝이 없는 맵'에 사용이 가능한 시스템이 완성된다.

이동하는 곳에 아이템이 동적으로 스폰이 가능해야한다. 무한대에 가까운 오브젝트를 미리 설치해 놓는 것은 불가능에 가깝다.

<예시 그림> 

편한 관리를 위해 바둑판 배열로 생성하기 위해 모든 타일의 크기는 같다. 각각의 타일의 위치는 리스트에 저장하여 중복 생성을 막는다. 

 

우선 타일을 생성할 때마다 이미 생성된 타일들이 저장되어있는 'fieldTiles' 리스트에서 각각 비교하여 확인하여 생성하려는 위치에 타일이 있는지 검사를 한다. 해당 위치에 타일이 없다면 (아이템 스폰) 타일을 생성한다.  

 

- 타일 중복 여부 검사 후 생성 메서드 실행

	if (CheckNullField(eachTilePos)) //해당 위치에 타일이 있는지 검사
	{ 
		CreateField(eachTilePos);
	}
    
    ------------------------------------------------------------------------------------
   
	bool CheckNullField(Vector3 newTilePos)
	{
		for (int i = 0; i < fieldTiles.Count; i++)
		{
			if (fieldTiles[i] == newTilePos) { return false; }
		}
		return true;
	}

 

- (아이템 스폰) 타일 생성 메서드

위에서 구현한 타일을 생성하는 메서드

가운데의 플레이어를 기준으로 만든 로컬 좌표 배열로 생성 타일의 위치를 정한다.


private Vector3[] creatTilesPos = new Vector3[]
{	new Vector3(-40f, 0f, 40f), new Vector3(0f, 0f, 40f), new Vector3(40f, 0f, 40f),
	new Vector3(-40f, 0f, 0),                             new Vector3(40f, 0f, 0f) ,
	new Vector3(-40f, 0f, -40f),new Vector3(0f, 0f, -40f),new Vector3(40f, 0f, -40f) 
};  

------------------------------------------------------------------------------------------

void CreateField(Vector3 newTilePos)
{
	GameObject newTile = Instantiate(fieldTilePrefab, newTilePos, Quaternion.identity, fieldTileParent);
	FieldTile newfieldTile = newTile.GetComponent<FieldTile>();

	if (newfieldTile != null)
	{
		fieldTiles.Add(newTilePos);
        }
}

 

 

- 구현 후 | Scene View

플레이어가 움직이는 위치에 따라 아이템이 스폰되는 타일이 생성된다.