프로젝트/미니게임파티

두 번째 미니게임 블록 깨기 개발 일지 2

쪼르뚜 2025. 5. 23. 16:48
728x90
반응형
  • 점수 기능 추가
  • 블록 생성 기능과 애니메이션 추가
  • 기능성 블록 프리팹 추가와 랜덤 배치 기능 구현

점수 기능

점수 기능은 '공이 블록에 부딪힐 때' 현재 점수에서 +50점이 되도록 구현하였다.

// BallContorller.cs

[HideInInspector] public UnityEvent OnPointScored;

private void OnCollisionEnter2D(Collision2D collision)
    // ... 생략
    else if (collision.gameObject.CompareTag("Brick"))
    // 공이 블록에 부딪혔을 때
    {
         OnPointScored.Invoke();
         // ... 생략

// GameManager.cs

private void Awake()
{
    // 이벤트 연결
    BallController.OnPointScored.AddListener(() => UpdateUI());
}

private void UpdateUI()
{
    score += 50;
    ScoreText.text = score.ToString();
}

블록 생성 기능과 애니메이션

처음에 블록이 4x7 배열로 생성되고 이후 블록이 모두 깨지면 새로운 4x7 배열이 위에서 내려오는 애니메이션을 추가했다.

각 블록을 동일한 간격으로 배치하고 각 줄마다 색상을 변경시켜 주었다.

private int rowMaxCount = 7;
private int columnMaxCount = 4;
private int maxSpecialBrickPerRow = 3;
private float brickSpacingX = 4;
private float brickSpacingY = 2;
private float initialSpawnY = 6.5f;
private float newSpawnY = 15;
private Color32[] brickMainColors = { GameColors.Salmon04, GameColors.Yellow04, GameColors.Olive04, GameColors.SkyBlue04 };

private void Awake()
{
    InitializeBricks(initialSpawnY);

    activeBrickCount = rowMaxCount * columnMaxCount;
}

private void InitializeBricks(float startY)
{
    activeBricks.Clear();
    int colorIndex = 0;

    for (int col = 0; col < columnMaxCount; col++)
    {
        for (int row = 0; row < rowMaxCount; row++)
        {
            // 미리 지정해둔 spacing 값으로 위치 계산
            float x = -((rowMaxCount - 1) * brickSpacingX) / 2f + row * brickSpacingX;
            float y = startY - col * brickSpacingY;
            GameObject brick = Instantiate(Brick, new Vector3(x, y, 0), Quaternion.identity, BrickContainer);
            brick.GetComponent<Brick>().SetColors(brickMainColors[colorIndex % brickMainColors.Length]);
            activeBricks.Add(brick);
        }

        colorIndex++;
    }
}

GameColors는 디자인 시스템의 색상 값을 이용하고 모든 게임에서 사용할 수 있도록 static class로 만들어주었다.

public static class GameColors
{
    public static readonly Color32 Salmon03 = new(253, 216, 211, 250);
    public static readonly Color32 Salmon04 = new(250, 128, 114, 250);
    public static readonly Color32 Salmon06 = new(200, 102, 91, 250);
    public static readonly Color32 Salmon07 = new(188, 96, 86, 250);
    public static readonly Color32 Salmon08 = new(150, 77, 68, 250);

    public static readonly Color32 Yellow03 = new(255, 253, 200, 250);
    public static readonly Color32 Yellow04 = new(255, 248, 77, 250);
    public static readonly Color32 Yellow06 = new(204, 198, 62, 250);
    public static readonly Color32 Yellow07 = new(191, 186, 58, 250);
    public static readonly Color32 Yellow08 = new(153, 149, 46, 250);

    public static readonly Color32 Olive03 = new(224, 240, 191, 250);
    public static readonly Color32 Olive04 = new(154, 205, 50, 250);
    public static readonly Color32 Olive06 = new(123, 164, 40, 250);
    public static readonly Color32 Olive07 = new(116, 154, 38, 250);
    public static readonly Color32 Olive08 = new(92, 123, 30, 250);

    public static readonly Color32 SkyBlue03 = new(218, 240, 249, 250);
    public static readonly Color32 SkyBlue04 = new(135, 206, 235, 250);
    public static readonly Color32 SkyBlue06 = new(108, 155, 176, 250);
    public static readonly Color32 SkyBlue07 = new(101, 155, 176, 250);
    public static readonly Color32 SkyBlue08 = new(81, 124, 141, 250);
}

 

블록이 모두 깨지면 (activeBricks == 0) 위에서 아래로 떨어지는 애니메이션을 실행해준다.

IEnumerator SpawnNewBlockWave()
{
    List<Vector3> startPositions = new();
    List<Vector3> targetPositions = new();

    // 블록의 시작 위치와 타겟 위치 저장
    foreach (var brick in activeBricks)
    {
        Vector3 startPos = new Vector3(brick.transform.position.x, brick.transform.position.y + newSpawnY, brick.transform.position.z);

        startPositions.Add(startPos);
        targetPositions.Add(brick.transform.position);

        brick.transform.position = startPos;
        brick.SetActive(true);
    }

    float duration = 0.5f;
    float elapsed = 0f;

    // 모든 블록을 타겟 위치까지 Vector3.Lerp를 이용하여 애니메이션처럼 이동
    while (elapsed < duration)
    {
        for (int i = 0; i < activeBricks.Count; i++)
        {
            activeBricks[i].transform.position = Vector3.Lerp(startPositions[i], targetPositions[i], elapsed / duration);
        }

        elapsed += Time.deltaTime;
        yield return null;
    }

    // 최종 위치 보정
    for (int i = 0; i < activeBricks.Count; i++)
    {
        activeBricks[i].transform.position = targetPositions[i];
    }
}

 

블록 생성 후 내려오는 애니메이션

기능성 블록 프리팹 추가와 랜덤 배치

우선 기본 블록 + 기능성 블록 8종류로 총 9가지 종류의 블록을 만들었다.

 

피그마에서 작업한 흔적...

 

블록 타입을 enum으로 정의하고 각 블록 프리팹에 색상을 설정할 수 있는 함수를 추가했다.

// BrickType.cs

public enum BrickType
{
    Basic = 0,
    Ball,
    Bomb,
    Hard,
    Heart,
    Minus,
    Plus,
    Safetynet,
    Transparent
}

// Brick.cs

public class Brick : MonoBehaviour
{
    public SpriteRenderer mainSprite;
    public SpriteRenderer subSprite;

    public void SetColors(Color32 mainColor, Color32 subColor)
    {
        mainSprite.color = mainColor;

        if(subSprite != null)
        {
            subSprite.color = subColor;
        }
    }
}

GameManager에서는 inspector 창에서 enum과 프리팹을 매치할 수 있도록 Serializable class를 정의했다.

그리고 inspector에서 설정한 후 Dictionary로 데이터를 변환시켜주었다.

[System.Serializable]
public class BrickEntry
{
    public BrickType type;
    public GameObject prefab;
}

public List<BrickEntry> Bricks;

private float[] blockWeights = new float[9]
{
    52f, 6f, 6f, 6f, 6f, 6f, 6f, 6f, 6f // 각 블록의 확률 가중치
};

private Dictionary<BrickType, GameObject> brickPrefabs = new();

private void Awake()
{
    foreach(var entry in Bricks)
    {
        if (!brickPrefabs.ContainsKey(entry.type)) // Dictionary로 변환
        {
            brickPrefabs.Add(entry.type, entry.prefab);
        }
    }
}

아까 작성해둔 블록 초기화 함수에서 타입을 key값으로 프리팹 value를 사용하도록 코드를 수정하면 된다.

private void InitializeBricks(float startY)
{
    activeBricks.Clear();
    System.Random rand = new();
    int colorIndex = 0;

    for (int col = 0; col < columnMaxCount; col++)
    {
        int specialCount = 0;

        for (int row = 0; row < rowMaxCount; row++)
        {
            // 코드 생략
            BrickType type;

            if(specialCount >= maxSpecialBrickPerRow) // 기능성 블록 갯수 제한
            {
                type = BrickType.Basic;
            }
            else
            {
                type = GetRandomBlockType(rand);

                if (type != BrickType.Basic)
                {
                    specialCount++;
                }
            }

            GameObject brick = Instantiate(brickPrefabs[type], new Vector3(x, y, 0), Quaternion.identity, BrickContainer);
            brick.GetComponent<Brick>().SetColors(brickMainColors[colorIndex % brickMainColors.Length], brickSubColors[colorIndex % brickSubColors.Length]);
            activeBricks.Add(brick);
        }

        colorIndex++;
    }
}

// 각 블록의 가중치를 적용하여 랜덤으로 블록 타입 설정
private BrickType GetRandomBlockType(System.Random rand)
{
    float randomValue = (float)(rand.NextDouble() * 100f);
    float cumulative = 0f;

    for (int i = 0; i < blockWeights.Length; i++)
    {
        cumulative += blockWeights[i];

        if (randomValue < cumulative)
        {
            return (BrickType)i;
        }
    }

    return BrickType.Basic;
}

 

한 줄에 최대 3개의 기능성 블록 생성

 

아직 기능성 블록들은 비주얼 뿐이라서 얼른 기능도 추가해야겠다!

 

728x90
반응형