상세 컨텐츠

본문 제목

2048 - choice 2 (개발)

개발일지

by go_ra_ni 2023. 8. 19. 00:44

본문

728x90

흐름도

메인 기능을 기획하고 흐름도를 작성한 후 코드를 작성했다.

 

먼저 다른 2048게임을 참고하여 메인 기능을 구성한다.

 

 

 

게임 개념 이해를 위한 그림 1

게임 규칙 

1. 같은 수의 블럭 2개가 맞닿아 있다면 합성한다.

2. 2개면 다음 단계의 블럭으로 3개면 2단계 위 블럭, 4개면 3단계 위 블럭으로 변한다. 

3. 이때 합성의 주체가 되는 위치의 기준은 새로 떨어진 블록 기준이다.

 

예를 들어 

게임 개념 이해를 위한 그림 2

빨간 블럭이 새로 떨어진 블럭이라면 그 위치에 새로운 블럭이 생성된다.

 

 

이 규칙을 바탕으로 흐름도를 작성한다.

 

2048 -choice 흐름도

대략적인 흐름도를 작성한 후 이를 바탕으로 코드를 작성했다.

 

구현을 최우선으로 하여 작성한 코드로 효율성은 낮을 수 있다.

이후 테스팅 과정에서 리펙토링하기로 한다.

 

큐브 생성 함수

 public void SpawnCube(int nowLine)
    {
        // 현재 줄이 다 찼는지 확인
        bool nowLineFull = true;
        // 현재 큐브 레벨 설정
        nowCubeLevel = nextCubeLevel[nowLine];
        // 스폰 제한 - 아직 로직 진행중일 시 새로 생성 불가능
        if (!canSpawn)
        {
            return;
        }
        canSpawn = false;

        SaveUndoData();

        // 빈 칸에 생성 할당 이동 후 머지 체크
        for (int i = 0; i < Y_COUNT; i++)
        {
            // 빈 곳이 있을 시
            if (square[nowLine, i] == null)
            {
                // 빈 곳 있음 표시
                nowLineFull = false;
                // 다음 큐브 정보 갱신
                
                AddNewCubeData();
                ShowNextCube();

                // 생성 후 하강 애니메이션
                square[nowLine, i] = Instantiate(cubes[nowCubeLevel]); // 판 변화
                SoundManager.instance.PlaySound("spawn");
                square[nowLine, i].transform.position = new Vector3(position_x[nowLine], 3.4f, 0);
                square[nowLine, i].transform.DOMoveY(position_y[i], 0.5f).OnComplete(() =>
                {
                    UpdateScore(nowCubeLevel);
                    // 머지 가능한지 확인
                    if (CheckMerge(nowLine, i))
                    {
                        // 머지 실행
                        StartCoroutine(Merge(nowLine, i));
                    }
                    else
                    {
                        // 새로 생성 가능
                        if (!EndCheck())
                        {
                            
                            canSpawn = true;
                        }
                    }
                    
                });
                break;
            }
        }

        // 빈 곳이 없을 시 마지막 큐브 머지가능한지 확인
        if (nowLineFull)
        {
            // 마지막 칸에서 머지할 경우
            if (square[nowLine, Y_COUNT - 1].GetComponent<Cube>().myLevel == nowCubeLevel)
            {
                Except1(nowLine, nowCubeLevel);
            }
            else
            {
                if (!EndCheck())
                {
                    
                    canSpawn = true;
                }
            }
        }
    }

상하좌우 확인 함수

private bool CheckMerge(int x, int y)
    {
        // 메인 위치 기준으로 머지 체크
        int nowLevel = square[x, y].GetComponent<Cube>().myLevel;
        

        bool canMerge = false;
        if (x > 0)
        {
            if (square[x - 1, y] != null && nowLevel == square[x - 1, y].GetComponent<Cube>().myLevel)
            {
                
                deleteList.Add(new Vector2Int(x-1,y));
                canMerge = true;
            }
        }
        if (y > 0)
        {
            if (square[x, y - 1] != null && nowLevel == square[x, y - 1].GetComponent<Cube>().myLevel)
            {
                
                deleteList.Add(new Vector2Int(x, y - 1));
                canMerge = true;
            }
        }
        if (x < X_COUNT-1)
        {
            if (square[x + 1, y] != null && nowLevel == square[x + 1, y].GetComponent<Cube>().myLevel)
            {
                
                deleteList.Add(new Vector2Int(x + 1, y));
                canMerge = true;
            }
        }
        if (y < Y_COUNT - 1)
        {
            if (square[x, y + 1] != null && nowLevel == square[x, y + 1].GetComponent<Cube>().myLevel)
            {
                
                deleteList.Add(new Vector2Int(x, y + 1));
                canMerge = true;
            }
        }
        return canMerge;
        
    }

큐브 합성 함수

private IEnumerator Merge(int x, int y)
    {
        
        // 삭제할 리스트에 본인 추가
        deleteList.Add(new Vector2Int(x, y));

        int tempNowLevel = square[x, y].GetComponent<Cube>().myLevel;

        if (deleteList.Count == 2)
        {
            tempNowLevel += 1;
        }
        else if (deleteList.Count == 3)
        {
            tempNowLevel += 2;
        }
        else if (deleteList.Count == 4)
        {
            tempNowLevel += 3;
        }
        // 제거 이펙트
        for (int i = 0; i < deleteList.Count; i++)
        {
            square[deleteList[i].x, deleteList[i].y].GetComponent<Animator>().SetBool("Delete", true);
        }
        SoundManager.instance.PlaySound("merge");
        yield return new WaitForSeconds(0.3f);
        // 제거
        for (int i = 0; i < deleteList.Count; i++)
        {
            Destroy(square[deleteList[i].x, deleteList[i].y]); // 판 변화 
        }

        deleteList.Clear();

        yield return new WaitForSeconds(0.1f);
        // 상위 큐브 생성
        if (cubes[tempNowLevel] == null)
        {
            yield break;
        }
        square[x, y] = Instantiate(cubes[tempNowLevel]); // 판 변화 
        square[x, y].GetComponent<Animator>().SetTrigger("Spawn");
        if (tempNowLevel > nowStageLevel + 9)
        {
            yield return StartCoroutine(LevelUp());
        }
        


        UpdateScore(tempNowLevel);

        square[x, y].transform.position = new Vector3(position_x[x], position_y[y], 0);
        
        // 현재 정보 다른 함수에게 넘길 수 있도록 프레임 넘기기
        yield return new WaitForEndOfFrame();

        // 자기자신이 하강하는지 안하는지 확인
        if (CheckMeDown(x, y))
        {
            SetDownList();
        }
        else
        {
            if (CheckMerge(x, y))
            {
                yield return new WaitForSeconds(0.2f);
                StartCoroutine(Merge(x, y));
            }
            else
            {
                SetDownList();
            }
        }
        
        
    }

자신이 하강하는지 확인하는 함수

 private bool CheckMeDown(int x, int y)
    {
        for (int i = 0; i < y; i++)
        {
            if (square[x, i] == null)
            {
                return true;
            }
        }
        return false;
    }

하강하는 큐브가 있는지 확인하는 함수

private void SetDownList()
    {
        downList.Clear();
        // 전체 큐브 중 하강하는 위치 정보 저장
        for (int i = 1; i < Y_COUNT; i++) // 세로 
        {
            for (int j = 0; j < X_COUNT; j++) // 가로
            {
                // 자기 자신보다 낮은 층에 원소가 비어 있으면 하강목록에 저장
                for (int k = 0; k < i; k++)
                {
                    if (square[j, i] != null && square[j, k] == null)
                    {
                        downList.Add(new Vector2Int(j, i));
                        break;
                    }
                }
            }
        }

        if (downList.Count > 0)
        {
            
            StartCoroutine(DownOne());
            return;
        }
        else
        {
            if (!EndCheck())
            {
                
                canSpawn = true;
            }
            
        }


    }

선택한 큐브 하강 동작 함수

private IEnumerator DownOne()
    {
        // 1개 하강
        
        for (int i = 0; i < downList[0].y; i++)
        {
            
            if (square[downList[0].x, i] == null)
            {
                
                square[downList[0].x, i] = square[downList[0].x, downList[0].y];
                square[downList[0].x, downList[0].y] = null;
                square[downList[0].x, i].transform.DOMoveY(position_y[i], 0.1f);
                yield return new WaitForSeconds(0.1f);
                // 머지 체크
                if (CheckMerge(downList[0].x, i))
                {
                    yield return new WaitForSeconds(0.2f);
                    // 머지 실행
                    StartCoroutine(Merge(downList[0].x, i));
                }
                else
                {
                    SetDownList();
                }
                yield break;
            }
        }
       

    }

 

이를 바탕으로 개발하여 현재 진행 상황이다.

 

 

728x90

관련글 더보기