프로젝트/미니게임파티
두 번째 미니게임 블록 깨기 개발 일지 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;
}
아직 기능성 블록들은 비주얼 뿐이라서 얼른 기능도 추가해야겠다!

728x90
반응형