2D 플랫포머 게임


개요

이 프로젝트는 2023년 수업 과제의 일환으로 한 달간 개발된 2D 플랫포머 모바일 게임입니다. 플랫폼 게임 장르의 핵심 플레이 요소를 포함하고 있으며, 전체 게임 흐름과 상태 전환을 관리하기 위해 간단한 FSM(Finite State Machine) 구조를 기반으로 구성되어 있습니다.

⚠️ 참고: 별도의 게임 기획서나 제공된 에셋 없이, 기획부터 시스템 구현, 외부 에셋 선별까지 모든 개발 과정을 스스로 진행했습니다.

저장소 보기
시연 보기


게임 소개

함정을 피하고, 움직이는 발판을 넘으며, 몬스터를 피하거나 처치해야 합니다. 5분 안에 3번의 목숨을 활용해 꼭대기의 빨간 깃발에 도달하는 것이 목표입니다.


핵심 기능

⚠️ 참고: 아래의 코드 예제는 간소화된 버전입니다. 전체 구현은 저장소에서 확인할 수 있습니다.

1. GameManager 클래스 (FSM 구조)

GameManager 스크립트의 함수들은 게임 실행 흐름을 제어하는 중앙 컨트롤러 역할을 하며, FSM(Finite State Machine) 패턴을 활용해 PLAY, PAUSE, GAMEOVER의 세 가지 주요 상태 간 전환을 처리합니다.

public class GameManager : MonoBehaviour
{
    enum eGameState { PLAY, PAUSE, GAMEOVER };
    eGameState m_StateBeforePause;
    eGameState m_GameState;
    
    void Start()
    {
        InPlay();
    }
    
	void Update()
	{
		switch (m_GameState)
		{
			case eGameState.PLAY:
				ModifyPlay();
				break;
			case eGameState.GAMEOVER:
				ModifyGameOver();
				break;
		}
	}
    
	// FSM Play //
    void InPlay()
    {
	    m_GameState = eGameState.PLAY;
    }
    void ModifyPlay()
    {
	    if (!Player.instance.IsAlive())
	    {
		    InGameOver();
		    return;
	    }
    }
 
	// FSM Pause //
	void InPause()
	{
		m_StateBeforePause = m_GameState;
		m_GameState = eGameState.PAUSE;
		
		Time.timeScale = 0.0f;
		UIPause.instance.Invoke_Pause(true);
		SoundManager.instance.PauseBGM();
	}
	void UnPause()
	{
		m_GameState = m_StateBeforePause;
		
		Time.timeScale = 1.0f;
		UIPause.instance.Invoke_Pause(false);
		SoundManager.instance.UnPauseBGM();
	}
 
	// FSM GameOver //
    void InGameOver()
    {
	    m_GameState = eGameState.GAMEOVER;
    }
    void ModifyGameOver()
    {
	    SceneManager.LoadScene("GameOverScene");
    }
}
InPlay(), InPause(), InGameOver()는 각 상태의 진입 지점 역할을 하며, Update()는 현재 게임 상태에 따라 ModifyPlay()ModifyGameOver()와 같은 적절한 상태 처리 함수를 매 프레임마다 호출합니다.


2. 타일맵 계층 구조

시각적 명확성개발 효율성을 동시에 확보하기 위해, 이 프로젝트의 타일맵 오브젝트들은 깔끔한 계층 구조로 구성되었습니다. 코드 기반은 아니지만, 이 설계 방식은 개발 구성 요소를 정리하고 일관된 렌더 순서를 유지하는 데 중요한 역할을 합니다.

이러한 타일맵 계층 구조빠른 반복 작업유지 보수에 용이한 씬 구성을 가능하게 했습니다.


3. 패럴랙스 스크롤링 배경

배경 레이어는 플레이어를 따라가는 카메라 위치에 따라 스크롤되며, 이로 인해 플레이어 움직임에 연동된 패럴랙스 효과를 보여줍니다.

이 시스템은 카메라 추적을 담당하는 CameraControl과 깊이에 따라 배경을 스크롤하는 BackgroundController 두 핵심 클래스를 통해 구현되었습니다.


CameraControl 클래스

public void TrackPlayer()
{
	Vector3 targetPos = new Vector3
	(
		m_Target.position.x + m_Offset.x,
		m_Target.position.y + m_Offset.y,
		m_FixedZ
	);
 
	transform.position = Vector3.Lerp(transform.position, targetPos, m_Smooth * Time.deltaTime);
 
	UpdateCameraBoundary();
 
	BackgroundController.instance.TrackBackGround();
}
카메라는 Lerp()를 사용해 플레이어를 부드럽게 따라가며, UpdateCameraBoundary()를 통해 자신의 위치를 맵 범위 안으로 제한하고, 마지막으로 매 프레임마다 배경 레이어를 갱신합니다.


BackgroundController 클래스

public void TrackBackGround()
{
	foreach (BackgroundInfo bgInfo in m_BackgroundList)
	{
		Vector3 pos = m_Cam.position + new Vector3(-m_Cam.position.x * bgInfo.m_Percent, 0, m_DepthZ);
		
		bgInfo.m_Background.position = pos;
	}
}
각 배경 레이어는 m_Percent 값에 따라 서로 다른 수평 속도로 이동합니다. 카메라에서 멀리 있는 레이어일수록 더 느리게 스크롤되어, 패럴랙스 스크롤링을 통해 깊이감을 형성합니다.


4. 플레이어 상호작용 영역들

이 부분에서는 적 밟기, 피해 입기, 아이템 수집, 지면 감지 등 플레이어가 환경 및 오브젝트와 상호작용하는 핵심 영역들을 설명합니다. 이는 정확한 판정과 기능별 충돌 처리를 유연하게 구현하기 위해 설계된 방식입니다.

  • 플레이어 오브젝트는 단일 콜라이더두 개의 감지 영역을 사용합니다:
    • 🟢 초록색 캡슐:
      • CapsuleCollider2D.
      • 지면과의 충돌만 감지.
      • 점프 상승 중에는 비활성화되며, 낙하 시 다시 활성화됨.
    • 🟨 노란색 박스:
      • 피격 감지 영역.
      • 함정, 적, 아이템만 감지.
    • 🟥 빨간색 박스:
      • 밟기 감지 영역.
      • 플레이어 아래의 적을 감지.
      • 착지 시 적을 처치하는데 사용됨.

PlayerController 클래스 - 핵심 함수들

함수 #1: 초록색 캡슐 - CapsuleCollider2D를 통한 지면 충돌 처리

bool IsGrounded()
{
	if (!m_PlayerFootCollider.enabled)
		return false;
 
	Collider2D groundCollider = Physics2D.OverlapArea(m_FeetPoint1.position, m_FeetPoint2.position, m_PlatformLayerMask);
 
	return groundCollider != null;
}
 
void UpdateFootColliderState()
{
	if (!IsGrounded())
	{
		if (m_Rb.velocity.y > 0.0f)
			m_PlayerFootCollider.enabled = false;
		else
			m_PlayerFootCollider.enabled = true;
	}
}
초록색 캡슐은 지면 충돌을 담당하는 주요 CapsuleCollider2D입니다. 플랫폼을 통과할 수 있도록 점프 상승 중에는 비활성화되며, 낙하 시 다시 활성화되어 착지를 감지합니다.


함수 #2: 노란색 박스 - "함정, 적, 아이템" 감지 영역

void CheckPlayerHitbox()
{
	Vector2 p1 = m_BodyPoint1.position;
	Vector2 p2 = m_BodyPoint2.position;
 
	// Death check
	Collider2D threat = Physics2D.OverlapArea(p1, p2, m_DeathLayerMask);
	if (threat != null)
	{
		PlayerManager.instance.LoseLife();
	}
 
	// Coin check
	Collider2D coin = Physics2D.OverlapArea(p1, p2, LayerMask.GetMask("Coin"));
	if (coin != null)
	{
		PlayerManager.instance.ObtainedCoin();
		Destroy(coin.gameObject);
		SoundManager.instance.PlaySFX("Coin");
	}
}
노란 박스는 Gizmo로 그려진 피격 감지 영역으로, 트랩, 적, 코인을 감지하는데 사용됩니다. 플랫폼은 무시되므로, 점프 시 플레이어가 이를 통과할 수 있습니다.


함수 #3: 빨간색 박스 - "적" 밟기 감지 영역

private void CheckEnemyUnderfoot()
{
	Collider2D enemy = Physics2D.OverlapArea(m_FeetPoint1.position, m_FeetPoint2.position, m_KillLayerMask);
	if (enemy == null)
		return;
 
	m_Rb.velocity = Vector3.up * m_JumpVelocity * 0.5f;
	PlayerManager.instance.KilledEnemy();
	Destroy(enemy.gameObject);
	SoundManager.instance.PlaySFX("EnemyKill");
}
빨간 박스는 플레이어의 발 아래에 있는 Gizmo로 그려진 밟기 감지 영역입니다. 하강 중 적을 감지하면, 적은 제거되고 플레이어는 약간 위로 튕겨 오릅니다.


맺는 말

이 프로젝트는 정밀한 충돌 처리와 트리거 로직을 통해 플레이어 상호작용게임 반응성을 개선하는 데 중점을 두었습니다. 비록 규모는 작지만, Unity의 2D 플랫폼 게임을 이해하고 확장 가능한 게임 기능을 구축하는 데에 탄탄한 기반이 되었습니다.