회전 로직을 너무 쉽게 생각했었네요.. 생각보다 어려웠습니다. 모든 타일을 왼쪽 상단을 기준으로 배열을 잡으려고 했는데, 막상 그렇게 만들어 놓으니 기존에 보던 테트리스 게임하고 괴리감이 커서 다시 수정을 하고, 그렇게 하니 또 회전 로직이 걸리고...ㅠ  회전 로직이 맘에 좀 안들지만... (다른 분들은 어찌했나 궁금하네요..) 이전 포스팅대로 대부분의 로직은 Block에서 거의 진행을 하고, GameManager는 전체적인 진행과 키입력에 따른 요청을 하고있습니다.

 Block의 소스가 중요로직이라고 생각되므로 Block과 키입력 예시부분만 포스팅을 하도록하죱. 


1. Block 

 - 블럭의 이동과 회전을 관리.

public class Block {
    public int[,,] tile;
    public Color32 color;
    
    int direct;
    int posX;
    int posY;

    public Block(int[,,] _tile, Color32 _color)
    {
        tile = _tile;
        color = _color;
    }
    public void SetPostion(int _x,int _y,ref int[,] _map)
    {
        int posX = _x;
        int posY = _y;

        MoveTile(direct, _x, _y, ref _map);
    }

    // 좌우로 움직임을 명령하는 함수. 외부에서 호출
    public void MoveHorizon(int _x, ref int[,] _map)
    {
        if (CheckTile(_x, _map))
        {
            MoveTile(direct,_x, 0,ref _map);
        }
    }

    // _x,_y 좌표로 이동하는 함수 ( 블럭을 이동시킬 때 사용)
    void MoveTile(int _direct, int _x, int _y, ref int[,] _map)
    {
        for(int i = 0; i < 4; i++)
        {
            for(int j = 0; j < 4; j++)
            {
                if(tile[direct, i, j] == 1)
                {

                    _map[posY + i, posX + j] = 0;
                }
            }
        }
        direct = _direct;
        posX += _x;
        posY += _y;

        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                if (tile[direct, i, j] == 1)
                {

                    _map[posY + i, posX + j] = tile[direct, i, j];
                }
            }
        }

    }

    // 좌우 좌표로 이동가능한지 확인하는 함수
    bool CheckTile(int _x, int[,] _map)
    {
        int tempX = posX + _x;
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                if (tile[direct, i, j] == 1 && !(_map[posY + i, tempX + j]==0 || _map[posY + i, tempX + j] == 1))
                {
                    return false;
                }
            }
        }
        return true;
    }


    // 블럭을 회전시키고 적용하는 함수. 회전시 맵을 삐져나갈 경우 때문에 새로 확인.
    public void RotationTile(ref int[,] _map)
    {
        // 회전 - 4가 되면 0으로 변경.

        int tempDirect = (direct +1) & 3;
        int count = CheckRotation(tempDirect,0, _map);
        if (count == -10)
        {
            return;
        }
        else if (count != 0)
        {
            // 옆 칸으로 이동해서 재확인.
            if (CheckRotation(tempDirect, count, _map)==0)
            {
                MoveTile(tempDirect, count, 0, ref _map);
            }
        }else
        {
            MoveTile(tempDirect, 0, 0, ref _map);
        }
    }

    // 블럭이 회전 가능한지 확인하는 함수. 회전했을때 가로로 겹쳐지는 길이를 반환한다.
    // 겹치는게 없다면 rotation을 하고, 겹쳐진다면 겹쳐진 길이만큼 이동시켜 재확인.
    // 1,1을 기준으로 왼쪽이 겹치면 +, 오른쪽은 -
    int CheckRotation(int _direct,int _x,int[,] _map)
    {
        int maxLeft = 0;
        int maxRight = 0;
        for (int i = 0; i < 4; i++)
        {
            int left = 0;
            int right = 0;
            for (int j = 0; j < 2; j++)
            {
                if (tile[_direct, i, j] == 1)
                {
                    int tempX = posX + _x + j;
                    if(tempX < 0)
                    {
                        left++;
                        continue;
                    }
                    if (!(_map[posY + i, tempX] == 0 || _map[posY + i, tempX] == 1))
                    {
                        left++;
                    }
                }
            }
            for (int j =3; j >=2; j--)
            {
                if (tile[_direct, i, j] == 1)
                {
                    int tempX = posX + _x + j;
                    if (tempX >= 12)
                    {
                        right++;
                        continue;
                    }
                    if (!(_map[posY + i, tempX] == 0 || _map[posY + i, tempX] == 1))
                    {
                        right ++;
                    }
                }
            }

            if (left > maxLeft)
            {
                maxLeft = left;
            }

            if(right> maxRight)
            {
                maxRight = right;
            }
        }
        if(maxRight == 0)
        {
            return maxLeft;
        }else if (maxLeft ==0)
        {
            return maxRight * -1;
        }else
        {
            return -10;
        }
    }
    // 블럭의 밑이 고정타일 또는 바닥인지 확인 후 바닥이 아니면 MoveTile을 이용해 내린다.
    // 바닥이면 applyGround 후 false를 리턴. - GM에서는 라인검색을 할 수 있도록.
    public bool MoveDown( int[,] _map)
    {
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                if (tile[direct, i, j] == 1 && _map[posY + i+1, posX + j] == -2)
                {
                    ApplyGround(ref _map);
                    return false;
                }
            }
        }
        MoveTile(direct,0, 1, ref _map);
        return true;
    }

    // 바닥에 닿은 블럭을 고정타일로 맵에 반영하는 함수.
    public void ApplyGround(ref int[,] _map)
    {
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                if (tile[direct, i, j] == 1 )
                {
                    _map[posY + i, posX + j] = -2;
                }
            }
        }
    }

    // 블럭을 바닥으로 한 번에 이동시키는 함수.
    // 블럭의 밑을 바닥까지 검사하여 가장 적은 거리만큼 이동 시키고 ApplyGround를 한다.
    public void DropTile(ref int[,] _map)
    {
        int min=30;
        for (int i = 0; i < 4; i++)
        {
            for (int j = 0; j < 4; j++)
            {
                if (tile[direct, i, j] == 1)
                {
                    int count = 0;
                    while (_map[posY + i + count +1, posX + j] != -2)
                    {

                        count++;
                    }
                    if (count < min)
                    {
                        min = count;
                    }
                }
            }
        }
        MoveTile(direct,0, min, ref _map);

    }
}



2. 키 입력에 따른 Block함수 사용 예시 ( GameManager의 일부)


        if (!isGameOver)
        {
            // 버튼 입력에 따른 테트리스 로직 실행.
            if (Input.GetKeyDown(KeyCode.RightArrow))
            {
                playerBlock.MoveHorizon(1, ref map);
            }
            if (Input.GetKeyDown(KeyCode.LeftArrow))
            {

                playerBlock.MoveHorizon(-1, ref map);
            }
            if (Input.GetKeyDown(KeyCode.UpArrow))
            {
                playerBlock.RotationTile(ref map);
            }
            if (Input.GetKeyDown(KeyCode.DownArrow))
            {
                if (playerBlock.MoveDown(map) == false)
                {
                    // 라인을 탐색하여, 가득차면 제거
                    // 천장을 넘어가면 게임 종료.
                    // 새로운 블럭생성.
                }
            }
            if (Input.GetKeyDown(KeyCode.Space))
            {
                playerBlock.DropTile(ref map);
                playerBlock.ApplyGround(ref map);
                // 라인을 탐색하여, 가득차면 제거
                // 천장을 넘어가면 게임 종료.
                // 새로운 블럭생성.
            }
        }




* 아래 동영상은 완성본의 일부 영상입니다.





* 지적은 언제나 환영입니다.

Posted by 검은거북

블로그 이미지
프로그래밍 공부 요약 및 공부하며 발생한 궁금증과 해결과정을 포스팅합니다.
검은거북

공지사항

Yesterday
Today
Total

달력

 « |  » 2025.1
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31

최근에 올라온 글

최근에 달린 댓글

글 보관함