공부의 기록/유니티 게임프로그래밍 입문

유니티(Unity) 게임프로그래밍 입문 5일차 수업

파티피플지선 2024. 12. 8. 17:34

이번주 목표

1. 마카롱 키우기 완성(폴리싱)

2. FPS 장르 유형 카메라 워킹 공부를 위한 미로 탈출 게임

 

 

<중요!!!!>

개발을 하면서 버그를 잡고 가는 것은 정말 중요하다.

버그를 제대로 잡고 가지 않으면, 작았던 버그가 괴물이 될 수 있다..!!!

<Tip>

주석처리 할 때

//[TODO]

같은 처리를 해 두면 나중에 코딩 점검 시 확인이 용이하다.

 

 

마카롱 합체

마카롱 프리팹 재구성

마카롱 충돌 체크

마카롱 충돌 처리 제거, 생성

 

 

GameScene 코드

더보기
using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class GameScene : MonoBehaviour
{
    [SerializeField] GameObject m_RespawnObj = null;
    [SerializeField] TextMeshProUGUI m_txtScore = null;
    [SerializeField] Deadline m_Deadline = null;
    [SerializeField] TextMeshProUGUI m_txtGameOver = null;

    GameObject m_RespawnMacaron = null;
    bool m_MouseClick = false;
    bool m_MouseRelease = false;

    const float RESPAWN_TIME = 3f;
    float m_RespawnTime = RESPAWN_TIME;

    [Header("DEBUG")]
    [SerializeField] TextMeshProUGUI m_txtMousePos = null;

    List<Macaron> m_ListMacaron = new List<Macaron>();

    const int RESPAWN_HIGH_LEVEL = 4;
    int m_HighLevel = 1;

    int m_Score = 0;

    const float DEAD_TIME = 2f; //[TODO][DEBUG] 1f로 원상 복구.
    float m_DeadlineTime = 0f;

    // Start is called before the first frame update
    void Start()
    {
        RespawnMacaron(1, false);

        if (null != m_Deadline)
        {
            m_Deadline.OnOffCollider(false);
        }

        m_Score = 0;

        if(null != m_txtGameOver)
        {
            m_txtGameOver.gameObject.SetActive(false);
        }
    }

    // Update is called once per frame
    void Update()
    {
        UpdateRespawnMacaron(); //move

        UpdateRespawnCheck(); //respawn

        UpdateMerging();

        UpdateDeadline();
    }

    void UpdateDeadline()
    {
        //데드라인 활성 코드.
        if(0f < m_DeadlineTime)
        {
            m_DeadlineTime -= Time.deltaTime;
            if(0f > m_DeadlineTime)
            {
                if(null != m_Deadline)
                {
                    m_Deadline.OnOffCollider(true);
                }
                m_DeadlineTime = 0f;
            }
        }

        //게임오버 확인 코드.
        if(IsGameOver())
        {
            if(null != m_txtGameOver &&
                false == m_txtGameOver.gameObject.activeSelf)
            {
                m_txtGameOver.gameObject.SetActive(true);
            }
        }
    }

    bool IsGameOver()
    {
        if(null != m_Deadline)
        {
            return m_Deadline.IsGameOver();
        }

        return false;
    }

    void UpdateMerging()
    {
        if (IsGameOver())
        {
            return;
        }

        //병합 후 사라지는 마카롱.
        /*for(int i = 0; i < m_ListMacaron.Count; ++i)
        {
            if (null != m_ListMacaron[i])
            {
                if (m_ListMacaron[i].GetDestroy())
                {
                    Destroy(m_ListMacaron[i].gameObject);
                    m_ListMacaron.RemoveAt(i);
                    break;
                }
            }
        }*/
        int safeCnt = 0;
        bool isDeleted = true;
        while (isDeleted &&
            1000 > safeCnt)
        {
            safeCnt++;
            isDeleted = false;

            for(int i = 0; i <m_ListMacaron.Count; i++)
            {
                if(null != m_ListMacaron[i])
                {
                    if (m_ListMacaron[i].GetDestroy())
                    {
                        Destroy(m_ListMacaron[i].gameObject);
                        m_ListMacaron.RemoveAt(i);

                        isDeleted = true;
                        break;
                    }
                }
            }
        }

        //머징 후 새로운 마카롱 생성.
        for(int i = 0; i < m_ListMacaron.Count; ++i)
        {
            if(null != m_ListMacaron[i])
            {
                if (m_ListMacaron[i].GetMerging())
                {
                    Vector3 pos = m_ListMacaron[i].transform.position;
                    Macaron macaron = RespawnMacaron(m_ListMacaron[i].GetLevel() + 1, true);
                    if(null != macaron)
                    {
                        macaron.transform.position = pos;
                        macaron.transform.localScale = Vector3.one * 0.1f;

                        Rigidbody2D rigid = macaron.GetComponent<Rigidbody2D>();
                        if(null != rigid)
                        {
                            rigid.gravityScale = 1f;
                        }

                        int addedScore = m_ListMacaron[i].GetLevel() + 1;
                        //m_Score += addedScore * addedScore;
                        m_Score += (int)Mathf.Pow(addedScore, 2);

                        RefreshUI();
                    }

                    m_RespawnMacaron = null;

                    Destroy(m_ListMacaron[i].gameObject);
                    m_ListMacaron.RemoveAt(i);
                    break;
                }
            }
        }
    }

    void RefreshUI()
    {
        if(null != m_txtScore)
        {
            m_txtScore.text = m_Score.ToString();
        }
    }

    void UpdateRespawnCheck()
    {
        if (IsGameOver())
        {
            return;
        }

        if (null == m_RespawnMacaron)
        {
            m_RespawnTime -= Time.deltaTime;
            if (0f > m_RespawnTime)
            {
                RespawnMacaron(GetRespawnLevel(), false);

                m_RespawnTime = RESPAWN_TIME;
            }
        }
    }

    int GetRespawnLevel()
    {
        int level = UnityEngine.Random.Range(1, m_HighLevel+1);
        if(RESPAWN_HIGH_LEVEL < level)
        {
            level = RESPAWN_HIGH_LEVEL;
        }

        return level;
    }

    void UpdateRespawnMacaron()
    {
        if(IsGameOver())
        {
            return;
        }

        if(null != m_RespawnMacaron)
        {
            m_MouseClick = false;
            m_MouseRelease = false;
            if (Input.GetMouseButtonDown(0))
            {
                m_MouseClick = true;
            }
            else if(Input.GetMouseButton(0))
            {
                m_MouseClick = true;
            }
            if(Input.GetMouseButtonUp(0))
            {
                m_MouseRelease = true;
            }

            Vector3 wpos = Vector3.zero;
            if(m_MouseClick)
            {
                if(null != m_txtMousePos)
                {
                    m_txtMousePos.text = $"{Input.mousePosition}";
                }

                wpos = Camera.main.ScreenToWorldPoint(Input.mousePosition);

                if(null != m_RespawnMacaron)
                {
                    wpos.y = m_RespawnMacaron.transform.position.y;
                    wpos.z = 0f;
                    m_RespawnMacaron.transform.position = wpos;
                }
            }
            if (m_MouseRelease)
            {
                if(null != m_RespawnMacaron)
                {
                    Rigidbody2D rigid = m_RespawnMacaron.GetComponent<Rigidbody2D>();
                    if(null != rigid)
                    {
                        rigid.gravityScale = 1f;
                    }

                    m_DeadlineTime = DEAD_TIME;
                    if(null != m_Deadline)
                    {
                        m_Deadline.OnOffCollider(false);
                    }

                    m_RespawnMacaron = null;
                }
            }
        }
    }

    Macaron RespawnMacaron(int inLevel, bool isMerging)
    {
        inLevel = Mathf.Clamp(inLevel, 1, 13);
        string macaronName = "Macaron";
        if(10 > inLevel)
        {
            macaronName += "0" + inLevel.ToString();
        }
        else
        {
            macaronName += inLevel.ToString();
        }

        string prefabPath = "Prefabs/" + macaronName;
        GameObject prefabObj = Resources.Load(prefabPath) as GameObject;
        if(null != prefabObj)
        {
            GameObject inst = Instantiate(prefabObj);
            if (null != inst)
            {
                inst.transform.parent = m_RespawnObj.transform;
                inst.transform.localPosition = Vector3.zero;

                if(isMerging)
                {
                    Quaternion qRot = Quaternion.identity;
                    Vector3 vRot = qRot.eulerAngles;
                    vRot.z = UnityEngine.Random.Range(0, 360);
                    qRot.eulerAngles = vRot;
                    inst.transform.localRotation = qRot;
                }
                
                Rigidbody2D rigid = inst.GetComponent<Rigidbody2D>();
                if(null != rigid)
                {
                    rigid.gravityScale = 0f;
                }
                Macaron macaron = inst.GetComponent<Macaron>();
                if(null != macaron)
                {
                    macaron.SetLevel(inLevel);
                }

                m_ListMacaron.Add(macaron);

                if (false == isMerging)
                {
                    m_RespawnMacaron = inst;
                }

                if(inLevel > m_HighLevel)
                {
                    m_HighLevel = inLevel;
                }

                return macaron;
            }
        }

        return null;
    }
}

 

Macaron 코드

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Macaron : MonoBehaviour
{
    int m_Level = 1;
    bool m_Merging = false; //이 기준으로 머징할 것. -> 파괴
    bool m_Destroy = false; //이 플래그면 파괴.

    const float DEFAULT_SCALE = 4f;

    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        UpdateScaling();

        //UpdateJingle();
    }

    void UpdateJingle()
    {
        Vector3 vRot = transform.localRotation.eulerAngles;
        vRot.z = UnityEngine.Random.Range(0, 20) - 10; //0~20 -> -10~10
        Quaternion qRot = Quaternion.identity;
        qRot.eulerAngles = vRot * Time.deltaTime;
        transform.localRotation = qRot;
    }

    void UpdateScaling()
    {
        Vector3 scale = transform.localScale;
        if (DEFAULT_SCALE > scale.x)
        {
            scale.x += Time.deltaTime * 10f;
            if (DEFAULT_SCALE < scale.x)
            {
                scale.x = DEFAULT_SCALE;
            }
        }
        if (DEFAULT_SCALE > scale.y)
        {
            scale.y += Time.deltaTime * 10f;
            if (DEFAULT_SCALE < scale.y)
            {
                scale.y = DEFAULT_SCALE;
            }
        }
        if (DEFAULT_SCALE > scale.z)
        {
            scale.z += Time.deltaTime * 10f;
            if (DEFAULT_SCALE < scale.z)
            {
                scale.z = DEFAULT_SCALE;
            }
        }
        transform.localScale = scale;
    }

    //private void OnCollisionEnter(Collision collision) <- 오류
    private void OnCollisionEnter2D(Collision2D collision)
    {
        if (null == collision ||
            null == collision.collider)
        {
            return;
        }

        if(GetDestroy() ||
            GetMerging())
        {
            return;
        }

        Macaron macaron = collision.collider.gameObject.GetComponent<Macaron>();
        if (null != macaron)
        {
            if(macaron.GetMerging() ||
                macaron.GetDestroy())
            {
                //pass
            }
            else if(macaron.GetLevel() == GetLevel())
            {
                Vector3 pos = macaron.transform.position;
                if(transform.position.y >= pos.y)
                {
                    SetMerging(true);
                    macaron.SetDestroy(true);
                }
                else
                {
                    macaron.SetMerging(true);
                    SetDestroy(true);
                }
            }
        }
    }

    public void SetLevel(int inLevel)
    {
        m_Level = inLevel;
    }
    public int GetLevel()
    {
        return m_Level;
    }

    public void SetMerging(bool inMerging)
    {
        m_Merging = inMerging;
    }
    public bool GetMerging()
    {
        return m_Merging;
    }

    public void SetDestroy(bool inDestroy)
    {
        m_Destroy = inDestroy;
    }
    public bool GetDestroy()
    {
        return m_Destroy;
    }
}

 

 

Deadline 코드

더보기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Deadline : MonoBehaviour
{
    BoxCollider2D m_Collider = null;

    bool m_GameOver = false;

    // Start is called before the first frame update
    void Start()
    {
        m_Collider = GetComponent<BoxCollider2D>();

        OnOffCollider(false);
    }

    public void OnOffCollider(bool inOn)
    {
        if(null != m_Collider)
        {
            m_Collider.enabled = inOn;
        }
    }

    // Update is called once per frame
    void Update()
    {
        
    }

    private void OnCollisionEnter2D(Collision2D collision)
    {
        if(null != collision)
        {
            Macaron macaron = collision.gameObject.GetComponent<Macaron>();
            if(null != macaron)
            {
                m_GameOver = true;
            }
        }
    }

    public bool IsGameOver()
    {
        return m_GameOver;
    }
}

 

 

 

 

FPS 형식의 미로탈출 게임 기획

미로 컨텐츠 만들기

미로는 2개 이상의 위치가 있고 랜덤한 시작점과 탈출점을 부여

탈출점 도달 시 다음 미로로 도달

1인칭 게임 기반 기술

 

미로 만들기 재밌당 ㅎ