[Unity2D] 새콤달콤 딸기농장 메인 오브젝트 개발(10) - 가게 미니게임 딸기를 받아라

SHG·2022년 10월 1일
1

이제 2종 중 마지막 게임인 미니게임3 딸기를 받아라의 개발일지이다!!

미니게임 구현에서 가장 중요한 점이 있었는데 바로 미니게임 4가지는 독립된 클래스가 아니라 미니게임 부모 클래스에서 상속받는 형태라는 것이다!! 코드를 첨부할 때 필요하다면 부모 클래스의 코드도 첨부하도록 하겠다.

위와 같이 하늘에서 떨어지는 딸기를 바구니로 받는 간단한 게임이며 딸기는 일반 딸기와 벌레가 붙은 딸기로 나누어진다.

아래는 MiniGameBerry Prefab이다.

MiniGameBerry.cs는 미니게임3에서 생성되는 딸기들에 대한 속도, 위치 등의 정보를 담고 있는 스크립트이다. 이 스크립트를 MiniGameBerry Prefab에 저장한 후 instance할 목적이다.

MiniGameBerry.cs

OnEnable()

private void OnEnable()
{
	// 화면 상단의 랜덤한 위치에 딸기 설정
    float rectXPos = Random.Range(150f, bgRect.rect.width - 150f);
    rect.anchoredPosition = new Vector3(rectXPos, bgRect.rect.height - 80f);
       
    minigame_3_score = this.gameObject.transform.GetComponentInParent<MiniGame3>().score;

    if (minigame_3_score < 100) // 점수에 따라 딸기 낙하속도 변경
    {
        berry_src_speed = 10.0f;
        berry_dst_speed = 20.0f;
    }
    else
    {
        berry_src_speed = 15.0f;
        berry_dst_speed = 25.0f;
    }
	//낙하 속도 할당
    velocity = Random.Range(berry_src_speed, berry_dst_speed);
}

프리팹이 활성화 될 때 마찬가지로 플레이어의 재미를 위해 일정 점수가 지나면 난이도를 증가시켰으며 화면 상단 랜덤한 위치에서 딸기가 떨어질 수 있도록 랜덤값을 주었다.

Update()

void Update()
{       
    if(MiniGame.isGameRunning)
    {
        // 딸기와 바구니의 거리를 계산
        float berry_x = rect.anchoredPosition.x + berry_rad, berry_y = rect.anchoredPosition.y + berry_rad;                    
        float basket_x = basketRect.anchoredPosition.x + 150f, basket_y = basketRect.anchoredPosition.y + 75f;
        float dist = (berry_x - basket_x) * (berry_x - basket_x) + (berry_y - basket_y) * (berry_y - basket_y);
            
        float r = berry_rad + basket_rad;
           
        
        // 딸기와 바구니의 거리를 두 점 사이의 거리 공식으로 계산한 뒤 
        // 두 객체의 반지름보다 작아졌을 때를 충돌했다고 판정
        if (dist <= r * r) // 딸기가 바구니에 닿았을 경우
        {
            // 벌레붙은 딸기를 먹었을 시
            if(this.gameObject.transform.GetChild(0).gameObject.activeSelf)
            {
                // 시간 감소!
                float size = this.gameObject.transform.GetComponentInParent<MiniGame3>().size;

                this.gameObject.transform.GetComponentInParent<MiniGame3>().scroll.fillAmount -= size * 10;
                this.gameObject.transform.GetComponentInParent<MiniGame3>().time -= 10;
                this.gameObject.transform.GetChild(0).gameObject.SetActive(false);
                AudioManager.instance.Cute5AudioPlay();
            }
            else // 정상 딸기를 먹었다면 점수 증가!
            {
                this.gameObject.transform.GetComponentInParent<MiniGame3>().score += 5;                                                    
                this.gameObject.transform.GetComponentInParent<MiniGame3>().score_txt.text
                    = this.gameObject.transform.GetComponentInParent<MiniGame3>().score.ToString();
                AudioManager.instance.Cute1AudioPlay();
            }

            this.gameObject.SetActive(false);
            return;
        }
        else if (berry_y <= 75f) // 딸기가 화면 밖으로 나갔다면 딸기를 비활성화 한다!
        {
            this.gameObject.transform.GetChild(0).gameObject.SetActive(false);
            this.gameObject.SetActive(false);
            return;
        }
        // 딸기의 속도만큼 딸기의 위치 정보를 갱신
        rect.anchoredPosition = new Vector3(rect.anchoredPosition.x, rect.anchoredPosition.y - velocity);           
    }
}

MiniGameBerry의 Update()에서 하는 주된 일은 바구니 충돌 여부 판정, 충돌 시 벌레 붙은 딸기인지 일반 딸기인지 판별, 화면 밖으로 딸기가 이탈했을 시 딸기 비활성화, 딸기의 속도에 따른 위치정보 갱신이다.

MiniGame3.cs 🎮

Awake() 👀

protected virtual void Awake() // 부모 클래스의 Awake()
{    
    // 미니게임 상단의 시간 제한을 나타내는 스크롤 바를 공통적으로 초기화 해준다.
    size = scroll.fillAmount / 60f;
    // 글로벌 변수를 공통적으로 tag를 기반으로 Find한다.
    global = GameObject.FindGameObjectWithTag("Global").GetComponent<Globalvariable>();
}

protected override void Awake() // 자식 클래스의 Awake()
{
    base.Awake();
    basketRect = basket.GetComponent<RectTransform>();     
}

앞서 만든 미니게임2와 같은 방식으로 부모 클래스의 Awake()에서 상속받았으며 바구니의 위치를 저장하는 basketRect()를 자식 클래스의 Awake()에서 할당해주었다.

Update()

public void PointDown()
{
    isDrag = true;
}
public void PointUp()
{
    isDrag = false;
}

void Update()
{
    if (!isGameRunning) return;

    int score = GetComponent<MiniGame3>().score;

    if (score >= 100) // 점수에 따라 딸기 생성주기 변경
    {
        minigame_3_src_rndtime = 0.3f;
        minigame_3_dst_rndtime = 0.6f;
    }
    else
    {
    	minigame_3_src_rndtime = 0.5f;
        minigame_3_dst_rndtime = 1.0f;
    }
    //딸기 생성 시간 결정!
    randTime = UnityEngine.Random.Range(minigame_3_src_rndtime, minigame_3_dst_rndtime);

    // 드래그해서 바구니 옮기기!
    if (isDrag)
    {                     
        Vector3 mousePos = Input.mousePosition;

        mousePos = camera.ScreenToViewportPoint(mousePos);
        mousePos.x = mousePos.x * bgRect.rect.width;

        float leftBorder = 0f;
        float rightBorder = bgRect.rect.width - basketRect.rect.width;           
            
        mousePos.y = bgRect.rect.height * 0.25f - 75f; // y좌표 고정
        mousePos.z = 0;
        if (mousePos.x < leftBorder) mousePos.x = leftBorder;
        else if (mousePos.x > rightBorder) mousePos.x = rightBorder;
        else mousePos.x = mousePos.x - basketRect.rect.width / 2;

        basketRect.anchoredPosition = Vector3.Lerp(basketRect.anchoredPosition, mousePos, 0.2f);
    }
    accTime += Time.deltaTime;

    // 랜덤한 시간마다 딸기 생성
    if(accTime >= randTime)
    {
        MinigameBerry miniBerry = GetMiniGameBerry();
        miniBerry.gameObject.SetActive(true);
        accTime = 0f;
    }
}

미니게임3은 기존 미니게임들과 다른점이 있는데 기존 미니게임들은 단위 시간(1초)마다 체크해주는 방식이었지만 미니게임3는 딸기가 떨어지는 현상을 Update()로 해줘야 했다.
또한 Update()에서 딸기가 상단에 생성되는 랜덤한 시간을 미니게임2와 마찬가지로 점수에 따라 변경하도록 하였고 바구니의 위치를 PointUp(), PointDown()을 통해 마우스의 위치로 갱신해주었다.

이 때 가장 중요한 문제에 직면하게 되었는데 딸기를 Instance, Destroy하는 방법은 게임에 부하를 주기 쉽다고 판단하였다. 그래서 이 부분에 오브젝트 풀링을 적용하여 최소한의 딸기만 Instance 하도록 구현하였다!

MakeMiniGameBerry() & GetMiniGameBerry()

MinigameBerry GetMiniGameBerry()
{
    for (int i = 0; i < berryPool.Count; i++)
    {
        if (!berryPool[i].gameObject.activeSelf)
        {
        	// 해금된 딸기 중 1개를 선별! 
            int rndId = unlockList[UnityEngine.Random.Range(0, unlockList.Count)];
            berryPool[i].GetComponent<Image>().sprite = global.berryListAll[rndId].GetComponent<SpriteRenderer>().sprite;
            
			// 벌레를 확률적으로 딸기에 생성!
            float bugrnd = UnityEngine.Random.Range(0f, 10f);
            if(bugrnd <= 2f)
            {             
                berryPool[i].transform.GetChild(0).gameObject.SetActive(true);
            }
            return berryPool[i].GetComponent<MinigameBerry>();
        }
    }
    return MakeMiniGameBerry(); // pool에서 가져올 딸기가 없다면 새로 만든다.
}

MinigameBerry MakeMiniGameBerry()
{
    GameObject instantMiniBerryObj = Instantiate(miniGameBerryPref, berryGroup);
    int rndId = unlockList[UnityEngine.Random.Range(0, unlockList.Count)];

    instantMiniBerryObj.GetComponent<Image>().sprite = global.berryListAll[rndId].GetComponent<SpriteRenderer>().sprite;
    instantMiniBerryObj.name = "MiniBerry " + berryPool.Count;

	// Instance한 딸기를 pool에 넣는다.
    berryPool.Add(instantMiniBerryObj);

    MinigameBerry instantMiniBerry = instantMiniBerryObj.GetComponent<MinigameBerry>();
    instantMiniBerry.bgRect = bgRect;
    instantMiniBerry.basketRect = basketRect;

    instantMiniBerry.transform.GetChild(0).gameObject.SetActive(false);

	//생성한 딸기를 리턴!
    return instantMiniBerry;
}

기본적인 오브젝트 풀링으로 Instance와 Destroy를 최소화하였고 벌레 생성 확률에 따라 벌레를 딸기에 달아주었다!!

플레이 영상으로 마무리~~!

profile
기록에 익숙해지자...!

1개의 댓글

comment-user-thumbnail
2022년 10월 13일

정말 멋지네요~ ^^

답글 달기