Unity - 2D 플랫포머 : 완성하기

TXMAY·2023년 8월 17일
0

Unity 튜토리얼

목록 보기
20/33
post-thumbnail

이번 강좌에서 2D 플랫포머 게임을 완성한다.


몬스터 잡기

슈퍼마리오에 굼바처럼 몬스터를 밟아서 잡을 수 있도록 만들어 보겠다.
다음과 같이 코드를 수정하고, 추가한다.

// PlayerMove.cs
...

void OnCollisionEnter2D(Collision2D collision)
{
    if (collision.gameObject.tag == "Enemy")
    {
        if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y)
        {
            OnAttack(collision.transform);
        }
        else
        {
            OnDamaged(collision.transform.position);
        }
    }
}

void OnAttack(Transform enemy)
{
    rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
    EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
    enemyMove.OnDamaged();
}
...

// EnemyMove.cs
...

CapsuleCollider2D capsuleCollider;

void Awake()
{
    ...
    
    capsuleCollider = GetComponent<CapsuleCollider2D>();
	...
    
}
...

public void OnDamaged()
{
    spriteRenderer.color = new Color(1, 1, 1, 0.4f);
    spriteRenderer.flipY = true;
    capsuleCollider.enabled = false;
    rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
    Invoke("DeActive", 5);
}

void DeActive()
{
    gameObject.SetActive(false);
}
...

Player가 몬스터를 밟으면 몬스터가 투명해지고 뒤집히면서 추락하게 된다.

아이템

코인 스프라이트를 이용하여 오브젝트를 생성한 후, Circle Collider와 애니메이션을 추가하고, 태그를 'Item'으로 설정한다.
그리고 Is Trigger를 활성화한 후, 'PlayerMove'에 다음과 같이 코드를 추가한다.

...

void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "Item")
    {
        collision.gameObject.SetActive(false);
    }
}
...

결승점

아이템과 같은 방식으로 생성한다. (Collider는 Box Collider를 사용하고, 태그는 'Finish'로 설정한다('Finish' 태그는 기본으로 있다))

게임 매니저 추가

Hierarchy에서 'Create Empty'를 눌러 게임 매니저로 사용할 오브젝트를 추가하고 'GameManager' 스크립트 파일을 생성한 후 추가하고, Box Collider를 추가한 후, Is Trigger를 체크하고 가로로 길게 늘려 지형 밑에 배치한다.
그리고 다음과 같이 코드를 작성하고, 추가한다.

// PlayerMove.cs
...

CapsuleCollider2D capsuleCollider;

void Awake()
{
    ...
    
    capsuleCollider = GetComponent<CapsuleCollider2D>();
}
...

void OnTriggerEnter2D(Collider2D collision)
{
    if (collision.gameObject.tag == "Item")
    {
        bool isBronze = collision.gameObject.name.Contains("Bronze");
        bool isSilver = collision.gameObject.name.Contains("Silver");
        bool isGold = collision.gameObject.name.Contains("Gold");

        if (isBronze)
        {
            gameManager.stagePoint += 50;
        }
        else if (isSilver)
        {
            gameManager.stagePoint += 100;
        }
        else if (isGold)
        {
            gameManager.stagePoint += 300;
        }
        collision.gameObject.SetActive(false);
    }
    else if (collision.gameObject.tag == "Finish")
    {
        gameManager.NextStage();
    }
}
...

public void OnDie()
{
    spriteRenderer.color = new Color(1, 1, 1, 0.4f);
    spriteRenderer.flipY = true;
    capsuleCollider.enabled = false;
    rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
}
...

Contains(비교문) : 대상 문자열에 비교문이 있으면 true
실행하면 아이템을 먹거나 몬스터를 밟으면 점수가 늘어나고 떨어지거나 몬스터와 부딪히면 라이프가 깎이고 0이되면 죽는다.
이 코드에서 떨어진 후 복귀하는 기능은 플레이어의 위치가 (0, 0)일 경우에만 가능하다.

스테이지

Player 오브젝트와 GameManger 오브젝트를 제외한 오브젝트를 한 오브젝트에 넣어(자식 오브젝트로 설정해) 정리한다.
이런 식으로 스테이지를 여러 개 생성한 후, 첫 번째 스테이지만 활성화한다.
그리고 다음과 같은 코드를 추가하고, 수정한다.

// PlayerMove.cs
...

public void VelocityZero()
{
    rigid.velocity = Vector2.zero;
}
...

// GameManager.cs
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public int totalPoint;
    public int stagePoint;
    public int stageIndex;
    public int health;
    public PlayerMove player;
    public GameObject[] Stages;

    public void NextStage()
    {
        if (stageIndex < Stages.Length - 1)
        {
            Stages[stageIndex].SetActive(false);
            stageIndex++;
            Stages[stageIndex].SetActive(true);
            PlayerReposition();
        }
        else
        {
            Time.timeScale = 0;
            Debug.Log("게임 클리어");
        }
        totalPoint += stagePoint;
        stagePoint = 0;
    }

    public void HealthDown()
    {
        if (health > 1)
        {
            health--;
        }
        else
        {
            player.OnDie();
            Debug.Log("죽었습니다!");
        }
    }
    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Player")
        {
            if (health > 1)
            {
                PlayerReposition();
            }
            HealthDown();
        }
    }
    void PlayerReposition()
    {
        player.transform.position = new Vector3(0, 0, -1);
        player.VelocityZero();
    }
}

메인 카메라는 Player 오브젝트의 자식 오브젝트로 넣는다.
그리고 Game Manager에 'Stages'라는 부분이 생겼는데, + 버튼을 누른 후, 스테이지 오브젝트를 놓으면 된다.
실행하면 스테이지 1, 2에선 골인 지점에 도달하면 다음 스테이지로 넘어가고 스테이지 3에선 '게임 클리어'라는 로그가 출력되며 멈춘다.

UI

앵커를 사용하여 체력, 스테이지, 점수 UI를 배치한다.
그리고 GameManager.cs에 다음과 같이 코드를 추가한다.

...

using UnityEngine.UI;

public class GameManager : MonoBehaviour
{
    ...
    
    public Image[] UIhealth;
    public Text UIPoint;
    public Text UIStage;
    public GameObject UIRestartBtn;

    void Update()
    {
        UIPoint.text = (totalPoint + stagePoint).ToString();
    }

    public void NextStage()
    {
        if (stageIndex < Stages.Length - 1)
        {
			...
           
            UIStage.text = "Stage" + (stageIndex + 1);
        }
        else
        {
            ...
            
            Text btnText = UIRestartBtn.GetComponentInChildren<Text>();
            btnText.text = "Clear!";
            ViewBtn();
        }
        ...
    }

    public void HealthDown()
    {
        if (health > 1)
        {
            ...
            
            UIhealth[health].color = new Color(1, 0, 0, 0.4f);
        }
        else
        {
            ...
            UIhealth[0].color = new Color(1, 0, 0, 0.4f);
            UIRestartBtn.SetActive(true);
        }
    }
    
    void ViewBtn()
    {
        UIRestartBtn.SetActive(true);
    }    
}

그리고 Inspector에서 변수에 해당하는 오브젝트들을 넣어준다.
게임 매니저를 버튼에 넣고, 재시작 함수를 만들어 OnClick() 함수와 연결한다.
버튼은 비활성화한다.

...

using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    ...

    public void Restart()
    {
    	Time.timeScale = 1;
        SceneManager.LoadScene(0);
    }
}

사운드

Player 오브젝트에 Audio Source 컴포넌트를 추가한다.
그리고 PlayerMove.cs에 다음과 같은 코드를 추가한다.

...

public class PlayerMove : MonoBehaviour
{
    public AudioClip audioJump;
    public AudioClip audioAttack;
    public AudioClip audioDamaged;
    public AudioClip audioItem;
    public AudioClip audioDie;
    public AudioClip audioFinish;
    ...
    
    AudioSource audioSource;

    void Awake()
    {
        ...
        
        audioSource = GetComponent<AudioSource>();
    }

    void Update()
    {
        if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping"))
        {
            ...
            
            PlaySound("JUMP");
        }
        ...
        
    }
	...

    void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "Item")
        {
            ...
            
            PlaySound("ITEM");
        }
        else if (collision.gameObject.tag == "Finish")
        {
           ...
           
            PlaySound("FINISH");
        }
    }

    void OnAttack(Transform enemy)
    {
        ...
        
        PlaySound("ATTACK");
    }

    void OnDamaged(Vector2 targetPos)
    {
        ...
        
        PlaySound("DAMAGED");
    }
    ...

    public void OnDie()
    {
        ...
        
        PlaySound("DIE");
    }
    ...

    void PlaySound(string action)
    {
        switch (action)
        {
            case "JUMP":
                audioSource.clip = audioJump;
                break;
            case "ATTACK":
                audioSource.clip = audioAttack;
                break;
            case "DAMAGED":
                audioSource.clip = audioDamaged;
                break;
            case "ITEM":
                audioSource.clip = audioItem;
                break;
            case "DIE":
                audioSource.clip = audioDie;
                break;
            case "FINISH":
                audioSource.clip = audioFinish;
                break;
        }
        audioSource.Play();
    }
}

그리고 Player Inspector에서 원하는 효과음을 넣으면 그 상황에 넣은 효과음이 재생된다.


이렇게 2D 플랫포머 게임 제작 강좌가 끝났다.
쉬운 부분도 있었고 어려운 부분도 있었지만 완성할 수 있어서 뿌듯했다.
이제는 개인 프로젝트도 만들어 보면서 포트폴리오를 만들어야겠다.

profile
게임 개발 공부하는 고양이

1개의 댓글

comment-user-thumbnail
2023년 8월 17일

좋은 글이네요. 공유해주셔서 감사합니다.

답글 달기