using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.U2D;

public class AtlasManager : MonoBehaviour
{
    public static AtlasManager instance;
    public SpriteAtlas blockAtlas;
    //싱글톤
    private void Awake()
    {
        //AtlasManager 클래스의 인스턴스를 instance에 할당
        instance = this;
    }
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.U2D;
using Random = UnityEngine.Random;

public class Test : MonoBehaviour
{
    public Board board;
    private void Start()
    {
        board.CreateBoard();
    }
    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
            Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 1);
            RaycastHit2D raycastHit2D = Physics2D.Raycast(ray.origin, ray.direction);
            if (raycastHit2D.collider != null)
            {
                Block block = raycastHit2D.collider.GetComponent<Block>();
                Debug.Log($"[{block.row}, {block.col}] ({block.transform.position.x}, {block.transform.position.y}), {block.blockType}");
            }
        }
    }
}

 

using Codice.Client.BaseCommands.Filters;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;

public class BlockSwapWindow : EditorWindow
{
    private int row1;
    private int col1;
    private int row2;
    private int col2;
    [MenuItem("Window/Block Swap Window")]
    
    public static void ShowWindow()
    {
        GetWindow<BlockSwapWindow>("Block Swap Window");
    }

    private void OnGUI()
    {
        GUILayout.Label("Swap Blocks", EditorStyles.boldLabel);

        row1 = EditorGUILayout.IntField("Row 1", row1);
        col1 = EditorGUILayout.IntField("Column 1", col1);
        row2 = EditorGUILayout.IntField("Row 2", row2);
        col2 = EditorGUILayout.IntField("Column 2", col2);

        //버튼 UI추가
        if (GUILayout.Button("Swap"))
        {
            Board.instance.SwapBlocks(row1, col1, row2, col2);
            Debug.Log($"Swapped blocks at [{row1}, {col1} ane [{row2}, {col2}]");
        }

    }

}

 

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(Block))]
public class BlockEditor : Editor
{
    public override void OnInspectorGUI()
    {
        base.OnInspectorGUI();

        Block block = target as Block;
        if (GUILayout.Button("제거"))
        {
            Debug.Log($"[{block.row},{block.col}]을 제거 합니다.");

            Board.instance.RemoveBlock(block.row, block.col);
        }
    }

    
}

 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Random = UnityEngine.Random;

public class Board : MonoBehaviour
{
    public static Board instance;// 싱글톤
    private Block[,] board; //2차원 배열이 Block들을 관리
    public GameObject blockPrefab;
    private int[] arrEmptySpaceCol;

    public int totalHeight = 10;
    public int height = 5;
    public int width = 9;
    private void Awake()
    {
        instance = this;
    }

    public void CreateBoard()
    {
        //크기가 9인 BlockType의 2차원 배열 만들기
        board = new Block[totalHeight, width]; // 2차원 배열로 변경
        //5행 9열의 Block타입의 2차원 배열 만들기
        for (int i = 0; i < height; i++) //board.GetLength(0)
        {
            for (int j = 0; j < width; j++) //board.GetLength(1)
            {
                CreateBlock(i, j);
            }
        }
        PrintBoard();
    }

    public void CreateBlock(int row, int col)
    {
        Vector2 pos = new Vector2(col, row);
        Block.BlockType blockType = (Block.BlockType)Random.Range(0, 5);
        GameObject blockGo = Instantiate(blockPrefab);
        Block block = blockGo.GetComponent<Block>();
        block.Init(blockType);
        block.SetPosition(pos);

        //배열의 요소에 블록 넣기
        board[row, col] = block;
    }

    public void RemoveBlock(int row, int col)
    {
        Block block = board[row, col];
        Destroy(block.gameObject); //게임 오브젝트 파괴

        //배열의 요소를 비워줌
        board[row, col] = null;
    }

    public void PrintBoard()
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < height; i++)    //행
        {
            for (int j = 0; j < width; j++)    //열
            {
                Block block = board[i, j];
                string strElement = (block == null) ? "null" : block.blockType.ToString();
                // 각 엘리먼트를 고정된 너비로 출력
                sb.AppendFormat("[{0},{1}] = {2,-11}", i, j, strElement);
            }
            sb.AppendLine();
        }
        Debug.Log(sb.ToString());
    }

    public void FindEmptySpaceFromColumn()
    {
        arrEmptySpaceCol = new int[width]; //배열 초기화

        for (int i = 0; i < width; i++) //열
        {
            int existBlockCnt = 0;
            for (int j = 0; j < totalHeight; j++) //행
            {
                Block block = board[j, i];
                if (block != null)
                {
                    existBlockCnt++;

                }
            }
            int emptySpace = height - existBlockCnt;
            arrEmptySpaceCol[i] = emptySpace; // 배열의 인덱스(열)에 emptySpace(카운트)값 할당 => 각 열마다 빈공간의 개수
            Debug.Log($"{i}열의 빈공간은 {emptySpace}개 입니다.");
        }

    }

    public void CreateNewBlocks()
    {
        if (arrEmptySpaceCol == null || arrEmptySpaceCol.All(x => x == 0))
        {
            Debug.Log("빈 공간이 없습니다.");
        }
        else
        {
            for (int i = 0; i < arrEmptySpaceCol.Length; i++)
            {
                int cnt = arrEmptySpaceCol[i];
                if (cnt > 0)
                {
                    Debug.Log($"{i}열에 {cnt}만큼 블록 생성 필요");
                    CreateBlockFromColumn(i, cnt);
                }
            }
        }
        arrEmptySpaceCol = null;
    }

    private void CreateBlockFromColumn(int col, int cnt)
    {
        int startRow = height; //행

        for (int i = 0; i < cnt; i++)
        {
            //[7, col] -> [8, col] -> [9, col]
            CreateBlock(startRow, col);
            startRow++;
        }
    }

    public void MoveAllBlocksInRow(int row)
    {
        if (row == 0)
        {
            Debug.Log("가장 아래 있는 행 입니다. (이동불가)");
            return;
        }

        int numberOfBlocksToMove = 0;

        for (int col = 0; col < width; col++)
        {
            int targetRow = row - 1;

            Block fromBlock = board[row, col];
            Block toBlock = board[targetRow, col];

            string strFromBlock = (fromBlock == null) ? "비어있음" : fromBlock.ToString();
            string strToBlock = (toBlock == null) ? "비어있음" : toBlock.ToString();



            if (fromBlock != null && toBlock == null)
            {
                //이동가능
                Debug.Log($"[{row},{col}] {strFromBlock} -> [{targetRow}, {col} {strToBlock}]으로 이동합니다");
                numberOfBlocksToMove++;
            }
            else
            {
                //이동 불가능
            }
        }
        if (numberOfBlocksToMove == 0)
        {
            Debug.Log($"{row} 행의 이동 블록이 업습니다.");
        }
        else
        {
            Debug.Log($"{row}행의 이동해야 할 블록이 {numberOfBlocksToMove}개 있습니다.");
        }
    }

    public void SwapBlocks(int row1, int col1, int row2, int col2)
    {
        if (board[row1, col1] == null && board[row2, col2] == null)
        {
            Debug.LogError($"[{row1}, {col1}]와 [{row2}, {col2}]가 모두 비어 있습니다. (스왑 실패)");
            return;
        }
        //위치 가져오기
        Vector2 fromPos = Block.Index2Position(row1, col1);
        Vector2 toPos = Block.Index2Position(row2, col2);

        //데이터 변경
        Block temp = board[row1, col1];
        board[row1, col1] = board[row2, col2];
        board[row2, col2] = temp;

        //비주얼 이동
        if (board[row1, col1] != null)
            board[row1, col1].SetPosition(fromPos);
        if (board[row2, col2] != null)
            board[row2, col2].SetPosition(toPos);

        Debug.Log($"Swapped blocks at [{row1}, {col1}] and [{row2}, {col2}]");

    }

}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using Random = UnityEngine.Random;

public class Board : MonoBehaviour
{
    public static Board instance;// 싱글톤
    private Block[,] board; //2차원 배열이 Block들을 관리
    public GameObject blockPrefab;
    private int[] arrEmptySpaceCol;

    public int totalHeight = 10;
    public int height = 5;
    public int width = 9;
    private void Awake()
    {
        instance = this;
    }

    public void CreateBoard()
    {
        //크기가 9인 BlockType의 2차원 배열 만들기
        board = new Block[totalHeight, width]; // 2차원 배열로 변경
        //5행 9열의 Block타입의 2차원 배열 만들기
        for (int i = 0; i < height; i++) //board.GetLength(0)
        {
            for (int j = 0; j < width; j++) //board.GetLength(1)
            {
                CreateBlock(i, j);
            }
        }
        PrintBoard();
    }

    public void CreateBlock(int row, int col)
    {
        Vector2 pos = new Vector2(col, row);
        Block.BlockType blockType = (Block.BlockType)Random.Range(0, 5);
        GameObject blockGo = Instantiate(blockPrefab);
        Block block = blockGo.GetComponent<Block>();
        block.Init(blockType);
        block.SetPosition(pos);

        //배열의 요소에 블록 넣기
        board[row, col] = block;
    }

    public void RemoveBlock(int row, int col)
    {
        Block block = board[row, col];
        Destroy(block.gameObject); //게임 오브젝트 파괴

        //배열의 요소를 비워줌
        board[row, col] = null;
    }

    public void PrintBoard()
    {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < height; i++)    //행
        {
            for (int j = 0; j < width; j++)    //열
            {
                Block block = board[i, j];
                string strElement = (block == null) ? "null" : block.blockType.ToString();
                // 각 엘리먼트를 고정된 너비로 출력
                sb.AppendFormat("[{0},{1}] = {2,-11}", i, j, strElement);
            }
            sb.AppendLine();
        }
        Debug.Log(sb.ToString());
    }

    public void FindEmptySpaceFromColumn()
    {
        arrEmptySpaceCol = new int[width]; //배열 초기화

        for (int i = 0; i < width; i++) //열
        {
            int existBlockCnt = 0;
            for (int j = 0; j < totalHeight; j++) //행
            {
                Block block = board[j, i];
                if (block != null)
                {
                    existBlockCnt++;

                }
            }
            int emptySpace = height - existBlockCnt;
            arrEmptySpaceCol[i] = emptySpace; // 배열의 인덱스(열)에 emptySpace(카운트)값 할당 => 각 열마다 빈공간의 개수
            Debug.Log($"{i}열의 빈공간은 {emptySpace}개 입니다.");
        }

    }

    public void CreateNewBlocks()
    {
        if (arrEmptySpaceCol == null || arrEmptySpaceCol.All(x => x == 0))
        {
            Debug.Log("빈 공간이 없습니다.");
        }
        else
        {
            for (int i = 0; i < arrEmptySpaceCol.Length; i++)
            {
                int cnt = arrEmptySpaceCol[i];
                if (cnt > 0)
                {
                    Debug.Log($"{i}열에 {cnt}만큼 블록 생성 필요");
                    CreateBlockFromColumn(i, cnt);
                }
            }
        }
        arrEmptySpaceCol = null;
    }

    private void CreateBlockFromColumn(int col, int cnt)
    {
        int startRow = height; //행

        for (int i = 0; i < cnt; i++)
        {
            //[7, col] -> [8, col] -> [9, col]
            CreateBlock(startRow, col);
            startRow++;
        }
    }

    public void MoveAllBlocksInRow(int row)
    {
        if (row == 0)
        {
            Debug.Log("가장 아래 있는 행 입니다. (이동불가)");
            return;
        }

        int numberOfBlocksToMove = 0;

        for (int col = 0; col < width; col++)
        {
            int targetRow = row - 1;

            Block fromBlock = board[row, col];
            Block toBlock = board[targetRow, col];

            string strFromBlock = (fromBlock == null) ? "비어있음" : fromBlock.ToString();
            string strToBlock = (toBlock == null) ? "비어있음" : toBlock.ToString();



            if (fromBlock != null && toBlock == null)
            {
                //이동가능
                Debug.Log($"[{row},{col}] {strFromBlock} -> [{targetRow}, {col} {strToBlock}]으로 이동합니다");
                numberOfBlocksToMove++;
            }
            else
            {
                //이동 불가능
            }
        }
        if (numberOfBlocksToMove == 0)
        {
            Debug.Log($"{row} 행의 이동 블록이 업습니다.");
        }
        else
        {
            Debug.Log($"{row}행의 이동해야 할 블록이 {numberOfBlocksToMove}개 있습니다.");
        }
    }

    public void SwapBlocks(int row1, int col1, int row2, int col2)
    {
        if (board[row1, col1] == null && board[row2, col2] == null)
        {
            Debug.LogError($"[{row1}, {col1}]와 [{row2}, {col2}]가 모두 비어 있습니다. (스왑 실패)");
            return;
        }
        //위치 가져오기
        Vector2 fromPos = Block.Index2Position(row1, col1);
        Vector2 toPos = Block.Index2Position(row2, col2);

        //데이터 변경
        Block temp = board[row1, col1];
        board[row1, col1] = board[row2, col2];
        board[row2, col2] = temp;

        //비주얼 이동
        if (board[row1, col1] != null)
            board[row1, col1].SetPosition(fromPos);
        if (board[row2, col2] != null)
            board[row2, col2].SetPosition(toPos);

        Debug.Log($"Swapped blocks at [{row1}, {col1}] and [{row2}, {col2}]");

    }

}

 

using System.Collections;
using System.Collections.Generic;
using TMPro;
using Unity.Mathematics;
using UnityEngine;

public class Block : MonoBehaviour
{
    public enum BlockType
    {
        Blue, Gray, Green, Pink, Yellow
    }

    public BlockType blockType;
    public SpriteRenderer spriteRenderer;
    public TMP_Text debugText;
    public int row;
    public int col;
    public void Init(BlockType blockType)
    {
        this.blockType = blockType;
        //이미지 변경 
        ChangeSprite(blockType);
    }

    public void ChangeSprite(BlockType blockType)
    {
        //블록의 이름을 넣어서 아틀라스에서 같은 이름인 sprite를 찾고 할당
        Sprite sp =
            AtlasManager.instance.blockAtlas.GetSprite(blockType.ToString());
        spriteRenderer.sprite = sp;
    }

    public void SetPosition(Vector2 pos)
    {
        transform.position = pos;
        var index = Position2Index(pos);
        row = index.row;
        col = index.col;
        debugText.text = $"[{index.row}, {index.col}]";
    }


    // 위치와 벡터 좌표가 다르기에 서로 교환가능하게 만들어주는 메소드
    public static (int row, int col) Position2Index(Vector2 pos)
    {
        return ((int)pos.y, (int)pos.x);
    }

    public static (int x, int y) Index2Position(Vector2 index)
    {
        return ((int)index.x, (int)index.y);
    }

    public static Vector2 Position2Index(int posX, int posY)
    {
        return new Vector2((int)posY, (int)posX);
    }

    public static Vector2 Index2Position(int row, int col)
    {
        return new Vector2((int)col, (int)row);
    }
}

완전 탐색을 하여 2중 for문을 돌린다

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MatchTest : MonoBehaviour
{
    private static readonly int Rows = 4;
    private static readonly int Cols = 4;
    
    private int[,] board =
    {
        {1, 1, 2, 0},
        {1, 1, 1, 1},
        {2, 1, 0, 1},
        {0, 0, 1, 1}
    };
    void Start()
    {
        FindMatchingBlocks();
    }
    
    private void FindMatchingBlocks()
    {
        HashSet<(int, int)> matchedBlocks = new HashSet<(int, int)>();

        for (int i = 0; i < Rows; i++)
        {
            for (int j = 0; j < Cols; j++)
            {
                CheckHorizontalMatch(i, j, matchedBlocks);
                CheckVerticalMatch(i, j, matchedBlocks);
            }
        }

        Debug.Log("Matching Blocks:");
        foreach (var block in matchedBlocks)
        {
            Debug.Log($"({block.Item1}, {block.Item2})");
        }
    }

    private void CheckHorizontalMatch(int row, int col, HashSet<(int, int)> matchedBlocks)
    {
        if (col + 2 < Cols &&
            board[row, col] == board[row, col + 1] &&
            board[row, col] == board[row, col + 2])
        {
            matchedBlocks.Add((row, col));
            matchedBlocks.Add((row, col + 1));
            matchedBlocks.Add((row, col + 2));
        }
    }

    private void CheckVerticalMatch(int row, int col, HashSet<(int, int)> matchedBlocks)
    {
        if (row + 2 < Rows &&
            board[row, col] == board[row + 1, col] &&
            board[row, col] == board[row + 2, col])
        {
            matchedBlocks.Add((row, col));
            matchedBlocks.Add((row + 1, col));
            matchedBlocks.Add((row + 2, col));
        }
    }
}

'산대특' 카테고리의 다른 글

플레이콘솔 리더보드에 점수 올리기  (0) 2024.06.26
2D 3Matchpuzzle - 매치 시 파괴 및 로드 + 힌트  (0) 2024.05.20
2D 3Matchpuzzle - 빈공간 찾기  (0) 2024.05.14
Download Python  (0) 2024.05.08
Learn Firebase  (0) 2024.05.02

TypeScript 프로젝트 환경 구성하기

 

1. 프로젝트 폴더 생성

2. 폴더 안으로 이동

mkdir (폴더명)
cd (폴더명)

 

2. 새로운 프로젝트 초기화

 

npm init -y

 

3. 프로젝트 내부에서 npm을 사용할 준비가 되었으므로, 이제 TypeScript를 설치

 

npm install typescript --save-dev

 

4. 프로젝트 루트 디렉토리에 tsconfig.json 파일을 생성, 이어 밑의 내용을 붙이기

 

 

5. 이제 src 폴더 밑에 TypeScript 언어로 된 파일을 작성 가능

src 폴더에 index.ts 파일을 만들어서 TypeScript 코드를 작성

 

 

6. 만약 TypeScript 언어를 JavaScript로 컴파일한 결과를 확인하고 싶다면 아래와 같은 명령어를 터미널에 입력

npx tsc --project ./tsconfig.json

명령어를 터미널에 입력하면 위와 같이 컴파일 된 결과가 dist 폴더에 들어가게 됨

 

TypeScript ESLint와 Prettier 설정하기

TypeScript는 2020년까지만 TSLint를 지원하고, 이후부터는 typescript-eslint를 사용해야 한다.

만일 TypeScript 프로젝트에서 ESLint나 Prettier를 사용하고 싶다면, 아래의 안내를 따른다.

1. VSCode 에디터를 사용할 것이므로, 확장 프로그램인 ESLint를 설치

2. 그리고 VSCode 에디터에 다음 설정을 적용, 이동하는 명령어 cmd + shift + p

{
  // ... 
  "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true
  },
  "eslint.alwaysShowStatus": true,
  "eslint.workingDirectories": [
      {"mode": "auto"}
  ],
  "eslint.validate": [
      "javascript",
      "typescript"
  ],
}

위의 내용은 사용자 설정에 넣어야 하며, 기존에 설치한 확장 프로그램들이 있다면 안이 채워져 있을 수 있다.

채워져 있다면 그 밑에 바로 내용을 넣어주면 된다.

 

 

3. 마지막으로 VSCode 에디터 설정 중 format on save가 설정되어 있는지 확인하고, 되어 있다면 설정을 해제

설정으로 이동하는 명령어는 cmd + ,

 

4. 이서서 Prettier 확장 프로그램도 설치

이어 몇 가지 필요한 라이브러리를 설치

npm i -D eslint prettier eslint-plugin-prettier @typescript-eslint/eslint-plugin @typescript-eslint/parser

 

 

6. 프로젝트 폴더 밑에 .eslintrc.js 파일을 만들고 이하 내용을 붙여 넣는다.

module.exports = {
  root: true,
  extends: [
    'plugin:@typescript-eslint/eslint-recommended',
    'plugin:@typescript-eslint/recommended',
  ],
  plugins: ['prettier', '@typescript-eslint'],
  rules: {
    'prettier/prettier': [
      'error',
      {
        doubleQuote: true,
        tabWidth: 2,
        printWidth: 80,
        bracketSpacing: true,
        arrowParens: 'avoid',
      },
    ],
    '@typescript-eslint/no-explicit-any': 'off',
    '@typescript-eslint/explicit-function-return-type': 'off',
    'prefer-const': 'off',
  },
  parserOptions: {
    parser: '@typescript-eslint/parser',
  },
};

해당 파일 내부에는 prettiertypescript-eslint에 대한 설정이 되어 있다.

rules 프로퍼티 내에 작성이 되어 있는 내용들은 옵션이며,

또한 필요하다면 React와 같은 라이브러리에도 해당 eslint가 적용될 수 있도록

env 같은 프로퍼티를 추가적으로 작성할 수 있다.

따라서 개발자의 취향에 따라 eslint 설정은 천차만별일 수 있다.

+ Recent posts