미니게임파티 프로젝트란...?
예전에 쥬니어네이버에 있던 동물농장이나 파니하우스와 같은 게임처럼 재화를 얻어 나만의 공간을 꾸미는 프로젝트를 만들고 싶었다. 컨셉을 정해서 하려고 했지만 무료 리소스는 한계가 있어 나중에 생각하기로 하고 미니 게임부터 만들어보자고~ 그렇게 시작된 프로젝트의 첫 번째 미니게임은 '플래피 버드'다!
플래피 버드 개발 과정
플래피 버드 게임는 단순히 클릭으로만 진행되는 게임으로 개발도 간단할거라 생각들어 첫 번째 미니게임으로 정했다. 실제로 플래피 버드 개발에는 약 11시간 정도 걸렸다. 프로젝트 시작하고 첫 날 기획과 전체적인 구조를 고려해서 코드를 작성했었는데 의존성 역전 원칙에 벗어나는거 같아서 싹 날렸다...😢 이런 부분은 디자인 패턴 공부를 더 하고 적용하는게 좋을거 같다는 생각이 들어 기능 위주로 먼저 작업하기로 했다. 그렇게 하나씩 기능을 만들다 보니 어느새 완성되었다!
간단한 코드 설명
스크립트는 게임 흐름을 관리하는 Gamemanager와 새를 관리하는 Bird, 파이프를 관리하는 Pipe로 구성되어 있다.
각 스크립트의 코드량이 많지 않아서 주석을 추가하여 포스팅 했다😎
public class Bird : MonoBehaviour
{
[HideInInspector] public UnityEvent OnTrigger;
public Animator animator;
private float gravityScale = 2f;
private float velocityY = 0f;
// 시간이 지남에 따라 서서히 아래로 떨어짐
private void Update()
{
float gravity = 9.8f * gravityScale;
velocityY += gravity * Time.deltaTime;
transform.Translate(velocityY * Time.deltaTime * Vector3.down);
}
// 새가 파이프나 벽에 충돌했을 때 실행
private void OnTriggerEnter2D(Collider2D collision)
{
OnTrigger?.Invoke();
}
// velocity값을 조절하여 새를 점프시킴
public void SetVelocityY(float newVelocityY)
{
velocityY = newVelocityY;
}
// 새의 애니메이션 조절하는 함수
public void PlayAnimation(string trigger)
{
animator.SetTrigger(trigger);
}
// 게임이 다시 시작됐을 때 새 초기화 함수
public void Reset()
{
PlayAnimation("Fly");
transform.position = new Vector3(-15, 0, 0);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class Pipe : MonoBehaviour
{
public UnityEvent OnBecameInvisible;
public UnityEvent OnPointScored;
public SpriteRenderer topRenderer;
public SpriteRenderer bottomRenderer;
public BoxCollider2D topCollider;
public BoxCollider2D bottomCollider;
private float moveSpeed = 10f;
private bool isPointed;
private void Update()
{
// 시간에 따라 왼쪽으로 이동
transform.Translate(moveSpeed * Time.deltaTime * Vector3.left);
// 파이프가 새의 위치를 넘게되면 점수 획득
if (transform.position.x < -33.2f && !isPointed)
{
OnPointScored?.Invoke();
isPointed = true;
}
// 파이프가 화면 밖으로 넘어가면
if (transform.position.x < -40f)
{
OnBecameInvisible?.Invoke();
}
}
// 파이프 초기화 함수
public void ResetPipe()
{
int topY = Random.Range(1, 30);
int bottomY = 30 - topY;
if (bottomY < 1)
{
bottomY = 1;
topY = 29;
}
topRenderer.size = new Vector2(7f, topY);
bottomRenderer.size = new Vector2(7f, bottomY);
topCollider.size = new Vector2(7, topY);
bottomCollider.size = new Vector2(7, bottomY);
topCollider.offset = new Vector2(0, -topY / 2);
bottomCollider.offset = new Vector2(0, bottomY / 2);
isPointed = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TMPro;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
[Header("게임오브젝트 초기 설정")]
public Transform PipePoolParent;
public GameObject PipePrefab;
public Bird bird;
[Header("UI 설정")]
public GameObject GameOverUI;
public TextMeshProUGUI CurrentScoreText;
public TextMeshProUGUI FinalScoreText;
public Button ButtonOK;
public Button ButtonRetry;
private int poolSize = 10;
private int score = 0;
private float spawnInterval = 1.5f;
private float jumpForce = 12;
private Vector3 spawnPoint = new(10, 0, 0);
private Queue<GameObject> pool = new();
private List<Pipe> activePipes = new();
// 이벤트 설정과 초기화
private void Awake()
{
InitializePool();
SpawnInitialPipes();
StartCoroutine(SpawnPipes());
bird.OnTrigger.AddListener(() => GameOver());
ButtonOK.onClick.AddListener(() => Debug.Log("OK"));
ButtonRetry.onClick.AddListener(() => Retry());
}
// 맨 처음에 파이프 풀에 파이프를 미리 생성
private void InitializePool()
{
for (int i = 0; i < poolSize; i++)
{
var pipeObject = Instantiate(PipePrefab, PipePoolParent);
pipeObject.SetActive(false);
pool.Enqueue(pipeObject);
var pipeScript = pipeObject.GetComponent<Pipe>();
pipeScript.OnBecameInvisible.AddListener(() => ReleasePipe(pipeObject));
pipeScript.OnPointScored.AddListener(() => {
score++;
UpdateUI();
});
}
}
// 생성된 파이프 풀에서 꺼내어 초기 세팅
private void SpawnInitialPipes()
{
for (int i = 0; i < 2; i++)
{
var pipe = GetPipe();
pipe.transform.position = spawnPoint + Vector3.left * 15 * i;
pipe.SetActive(true);
}
}
// 아무 키가 눌리면 새가 점프
private void Update()
{
if (Input.anyKeyDown)
{
bird.SetVelocityY(-jumpForce);
}
}
// 파이프 풀에서 파이프 가져오는 함수
public GameObject GetPipe()
{
if (pool.Count > 0)
{
var pipeObject = pool.Dequeue();
pipeObject.SetActive(true);
var pipeScript = pipeObject.GetComponent<Pipe>();
pipeScript.ResetPipe();
activePipes.Add(pipeScript);
return pipeObject;
}
else
{
var pipeObject = Instantiate(PipePrefab, PipePoolParent);
var pipeScript = pipeObject.GetComponent<Pipe>();
pipeScript.OnBecameInvisible.AddListener(() => ReleasePipe(pipeObject));
pipeScript.OnPointScored.AddListener(() => {
score++;
UpdateUI();
});
activePipes.Add(pipeScript);
return pipeObject;
}
}
// 파이프 풀에 파이프 집어 넣는 함수
public void ReleasePipe(GameObject pipeObject)
{
pipeObject.SetActive(false);
pool.Enqueue(pipeObject);
Pipe pipeToRemove = null;
foreach(var pipe in activePipes)
{
if (pipe.gameObject == pipeObject)
{
pipeToRemove = pipe;
break;
}
}
if (pipeToRemove != null)
{
activePipes.Remove(pipeToRemove);
}
}
// 일정시간 동안 파이프 생성 시켜주는 코루틴
IEnumerator SpawnPipes()
{
while (true)
{
yield return new WaitForSeconds(spawnInterval);
SpawnPipe();
}
}
// 파이프 생성 함수
private void SpawnPipe()
{
var pipe = GetPipe();
pipe.transform.position = spawnPoint;
}
// 점수 UI 갱신 함수
private void UpdateUI()
{
CurrentScoreText.text = score.ToString();
}
// 게임 오버 했을 때 세팅
private void GameOver()
{
bird.enabled = false;
bird.PlayAnimation("Hit");
foreach(var pipe in activePipes)
{
pipe.enabled = false;
}
StopAllCoroutines();
CurrentScoreText.gameObject.SetActive(false);
GameOverUI.SetActive(true);
FinalScoreText.text = CurrentScoreText.text;
}
// 다시 시작할 때 초기화 함수
private void Retry()
{
score = 0;
CurrentScoreText.text = "0";
CurrentScoreText.gameObject.SetActive(true);
GameOverUI.SetActive(false);
bird.enabled = true;
bird.Reset();
List<Pipe> pipesToRelease = new List<Pipe>(activePipes);
foreach (var pipe in pipesToRelease)
{
pipe.enabled = true;
ReleasePipe(pipe.gameObject);
}
activePipes.Clear();
InitializePool();
SpawnInitialPipes();
StartCoroutine(SpawnPipes());
}
}
디자인
UI를 넣어야 하는데 마음에 드는게 없어서 피그마에서 플러그인을 통해 색상 팔레트를 만들고 간단한 UI 배경을 만들었다.
완성된 모습
현재는 극악 난이도로 설정되어 있다 ㅋㅋ 디테일한 부분은 더 잡아야할거 같은데 우선은 마무리지었다.
나중에 재화 시스템을 넣게되면 그 때 난이도 조절을 하면서 디테일한 부분을 더 잡아야할듯!
원래는 하루 작업할 때마다 포스팅하려고 했는데 미루게 되어서 이렇게 한 번에 완성된 과정만 작성하게 되었다.
근데 사실 중간에 딱히 어려운 부분은 없어서 빨리 완성시키고 싶어서 그런거 같기두...
여하튼 포스팅은 미룰수록 더더욱 귀찮은거 같다... 다음 미니 게임은 자주 포스팅 해야지!!

'프로젝트 > 미니게임파티' 카테고리의 다른 글
두 번째 미니게임 블록 깨기 개발 일지 4 (1) | 2025.06.02 |
---|---|
두 번째 미니게임 블록 깨기 개발 일지 3 (0) | 2025.05.28 |
두 번째 미니게임 블록 깨기 개발 일지 2 (2) | 2025.05.23 |
두 번째 미니게임 블록 깨기 개발 일지 1 (1) | 2025.05.13 |