Notice
Recent Posts
Recent Comments
Link
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개임 게발

[Unity] [고양이 밥주기] 애니메이션, HP바 만들기, 고양이 밥 먹이기, 고양이 반복 생성, Retry 버튼 만들기 본문

강의 정리

[Unity] [고양이 밥주기] 애니메이션, HP바 만들기, 고양이 밥 먹이기, 고양이 반복 생성, Retry 버튼 만들기

lhgenol 2025. 2. 2. 23:49

고양이 만들기 - 고양이와 배부른 고양이(3-3)

고양이 만들기

  • 이제 본격적으로 고양이를 만들고 고양이가 내려오면 밥을 먹여주는 로직을 만들자. 일단 고양이부터
  • Create Empty -> 'NomalCat' 생성 -> Transform은 리셋 -> NomalCat 안에다 Sprites -> Square 생성 -> 'Hungry' (배고픈 고양이)
  • Full(배부른) 고양이도 똑같이 추가하고 Full -> normaCat_full, Hungry -> normaCat_1 이미지 적용

HP바 만들기

  • 이제 이 고양이가 제대로 배가 불렀는지 알아볼 수 있는 HPBar를 만들자.
  • Hungry에 UI -> Image 생성
  • Canvas의 Render Mode가 Overlay라고 되어 있다. 이 Overlay는 휴대폰이면 휴대폰 기계 스크린에, 노트북이면 노트북 스크린에 직접적으로 그려진다고 생각하면 됨. 이걸 World Space로 바꿔주자.
  • Canvas 크기를 조절해 주고, Image(Back)의 Center도 Shift + Option -> 맨 우측 하단 버튼 클릭해 주면 Canvas 사이즈에 맞춰서 Image도 맞춰주는 모습을 볼 수 있다.
  • Back 이미지를 복사해 'Front'를 만들어 주고, Anchors(기준점)를 x 0으로 맞춰준다.
  • 이제 Scale을 내려주면, HP바가 줄어드는 듯한 모습이 보인다. 먹이를 먹을 때마다 이 Scale값을 조금씩 늘려 주면 되겠다.

애니메이션 만들기

  • 이제 고양이에게 적용시켜 줄 애니메이션을 만들자.
  • Ceate -> Animation Clip -> 'NormalCat' -> Loop 체크
  • NormalCat 클립을 Hungry에 드래그 드롭 -> 'Normal'
  • 그러면 애니메이션 컨트롤러도 생김. Hungry에 가면 Animator도 붙어 있는 걸 볼 수 있다.
  • NormalCat 클립 열고 녹화 on -> 0에 normaCat_1 -> 10에 normaCat_2 -> 20에 normaCat_2 -> 녹화 off
  • 재생해 보면 고양이가 까딱까딱거린다.
  • 뚱뚱한 고양이(FatCat) 역시 NormalCat 오브젝트를 복사해 만들자.
  • NormalCat 오브젝트를 Prefabs 폴더로 가져가 Prefab화 해주고 삭제
  • FatCat 이미지를 각각 적용해 주고, 기존에 있던 애니메이션은 삭제해 준다.
  • Animations 폴더에 'FatCat' 클립 생성 후 Loop 체크 -> FatCat의 Hungry에 드래그 드롭 -> 'Fat'
  • FatCat 애니메이션도 NormalCat과 똑같이 적용하고, Prefab화 해준다.
  • 이렇게 일반 고양이, 뚱뚱한 고양이 애니메이션 Prefab을 모두 만들었다.

고양이 만들기 - 중력, HP바(3-4)

고양이를 내려주기

  • 이제 고양이들을 위에서부터 내려줘야 한다. 고양이가 내려옴 -> 강아지는 밥을 쏨 -> 밥에 고양이가 충돌했을 때 HP바를 올려줌
  • 먼저 고양이를 내려주기 위해 Cat 스크립트를 만들자.
    void Update()
    {
        transform.position += Vector3.down;
    }
  • Vector3.down: 계속해서 Position y값에 마이너스 값이 더해진다.
  • NormalCat을 하나 꺼내오고, 여기에 Cat 스크립트를 붙여준 다음 플레이 해보면 너무 빨리 내려간다.
    void Update()
    {
        transform.position += Vector3.down * 0.05f;
    }
  • 0.05를 곱해 속도를 낮춰줬다.
  • 그럼에도 사양에 따라 너무 빨리 내려올 수 있으니 프레임 값을 고정해 주자.
    void Start()
    {
        Application.targetFrameRate = 60;
    }
  • 이건 나중에 GameManager 스크립트에 옮길거임

고양이 밥 먹이기

  • 이제 고양이와 밥이 충돌하는 로직을 만들자.
  • 고양이에게 Box Collider 2D 추가 -> Collider 크기 조절
  • 현재는 Hieararchy 창에서만 수정했기 때문에 Prefab에도 적용을 시켜줘야 한다. 인스펙터 창의 Overrides 버튼 클릭 -> Apply All 클릭
  • 이러면 Prefab에도 적용이 된다.
  • 밥에는 Circle Collider 2D와 RigidBody 2D 추가
  • 플레이 해보면 밥이 덜덜덜덜덜 떨면서 올라갔다가 다시 떨어지기도 하고 난리났다.
  • 왜냐면 RigidBody를 쓰면 중력에 영향을 받기에 올라가다가 떨어짐. 그래서 이 중력을 꺼줘야 한다.
  • Food Prefab -> RigidBody -> Body Type을 Dynamic이 아닌 Kinematic으로 변경
  • 이렇게 해주면 더 이상 중력의 영향을 받지 않는다.
  • 그런데 문제는 Collider 2D랑 부딪혔을 때의 로직도 동작하기 않게 됨. 물리 영향을 안 받게 되는 것. 그래서 이때는 Is Trigger를 체크해줘야 함 (Cat도 같이)
  • Food에 새로운 태그 Food를 만들어 적용시켜 줌
  • 이제 고양이와 밥이 만났을 때 충돌을 했는지 안 했는지 판단을 해야한다. 이 로직을 Cat 스크립트에 만들자.
  • 그동안은 onCollisionEnter2D 메소드를 통해서 충돌 여부를 체크했는데, Kinematic으로 변경했기 때문에 CollisionEnter는 이제 못 쓴다.
    private void OnTriggerEnter2D(Collider2D collision)
    {

    }
  • 그래서 이번엔 OnTriggerEnter2D를 사용한다.
  • 사용 방법은 같음. 매개변수에 있는 collision에 부딪힌 Collider의 정보들이 담겨 있기 때문
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            Debug.Log("맛있다.");
        }
    }
  • Collision에 있는 정보를 꺼내오기 위해 부딪힌 게임 오브젝트의 태그값을 가져왔다.
  • Food와 충돌했을 때를 확인할 디버그 로그도 작성해줬다.
  • 플레이 해보면, 고양이와 밥이 충돌할 때마다 "맛있다" 로그가 찍힌다.
  • 이번에는 충돌됐을 때 고양이 밑에 있는 HPBar를 늘려줘야 한다.
  • NormalCat -> Front -> Rect Transform의 Scale값을 키워주면 되겠다.
public class Cat : MonoBehaviour
{
    public RectTransform front;
  • Cat 스크립트로 돌아가서 RectTransform을 public 해줬다.
  • 이제 게이지를 올려줘야 한다. 게이지는 전체 값이 있을 거고 Food에 맞을 때마다 일정하게 올려주는 값이 있을 것. (ex. 전체값 5, 부딪힐 때마다 1씩 올라간다)
  • 이 부분을 만들어 주기 위해 변수를 두 개 생성하자.
public class Cat : MonoBehaviour

{
    public RectTransform front;
    float full = 5.0f;
    float energy = 0.0f;
  • 이제 food에 충돌할 때마다 이 energy를 1씩 올려주자.
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            energy += 1.0f;
        }
    }
  • 디버그 로그 대신 energy에다가 1씩 더해준다.
  • 이제 이 값을 RectTransform, Front 게이지에다가 반영해 줘야 함
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            energy += 1.0f;
            front.localScale = 
        }
    }
  • 전체 값에서 현재 energy값을 나눠주면 되니 energy / 전체 값으로 해주면 됨. 그리고 이걸 Scale의 x값에 넣어줘야 함
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            energy += 1.0f;
            front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
        }
    }
  • 이제 고양이한테 맞은 밥은 파괴시켜줘야 한다.
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            energy += 1.0f;
            front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
            Destroy(collision.gameObject);
        }
    }
  • Destroy까지 추가
  • NormalCat의 Front 변수에 Front까지 추가(+Prefab Apply)하고 플레이 해보면, 고양이와 충돌했을 때 밥은 사라지고 게이지가 올라가는 것도 볼 수 있다.
  • 그런데 HPBar가 다 찼는데도 끝도 없이 늘어난다. 이 부분을 수정하자.
  • energy값과 full값이 서로 같게 되면, 즉 energy값이 full값보다 작을 때만 HPBar를 늘려주면 되겠다.
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            if(energy < full)
            {
                energy += 1.0f;
                front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                Destroy(collision.gameObject);
            }
            else
            {
                Debug.Log("배가 불러요");
            }
        }
    }
  • energy가 full보다 작으면 계속 늘려줘야 하니 기존 수식 그대로 옮겨주고, else문에는 일단 디버그 로그를 작성했다.
  • 확인해 보니 게이지가 다 차면 "배가 불러요"로그가 뜨고 게이지바는 멈춘다.
  • 계속 나오는 "배가 불러요" 로그를 한 번만 차게 하고, Hungry 오브젝트 대신 Full 오브젝트를 켜줘야 함
public class Cat : MonoBehaviour

{
    public GameObject HungryCat;
    public GameObject FullCat;
  • 먼저 HungryCat, FullCat 변수를 만들어 줬다.
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            if(energy < full)
            {
                energy += 1.0f;
                front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                Destroy(collision.gameObject);
            }
            else
            {
                HungryCat.SetActive(false);
                FullCat.SetActive(true);
            }
        }
    }
  • 그리고 디버그 로그 대신 HungryCat 게임 오브젝트를 꺼주고, FullCat 게임 오브젝트를 켜준다.
  • 변수에 적용(+Prefab Apply)하고 플레이 해보면, 게이지가 다 찼을 때 더 이상 올라가지 않으면서 FullCat 이미지로 바뀌는 것을 볼 수 있다.
  • 이제 HPBar가 다 차면 고양이가 옆으로 이동하는 로직을 만들자.
    void Update()
    {
        if(energy < full)
        {
            transform.position += Vector3.down * 0.05f;
        }
        else
        {

        }
    }   
  • energy가 full보다 작을 때는 기존 그대로 아래로 내려오게끔 똑같이 옮겨준다.
  • 아닐 때는 왼쪽에 있을 때 기준 왼쪽으로, 오른쪽에 있을 때 기준 오른쪽으로 이동하게 해 주자.
  • 가운데 값이 0일 때 고양이 Position의 x값이 양수면 오른쪽에 있는 것. 반대로 음수면 왼쪽에 있는 것이기 때문에 각각 오른쪽과 왼쪽으로 이동시켜주면 되겠다.
    void Update()
    {
        if(energy < full)
        {
            transform.position += Vector3.down * 0.05f;
        }
        else
        {
            if(transform.position.x > 0)
            {
                transform.position += Vector3.right;
            }
            else
            {
                transform.position += Vector3.left;
            }
        }
    }  
  • x값이 0보다 크면 오른쪽으로 이동, 아니면 왼쪽으로 이동
  • 플레이 해보면 왼쪽으로 이동하긴 하는데 너무 빨리 이동한다.
   void Update()
    {
        if(energy < full)
        {
            transform.position += Vector3.down * 0.05f;
        }
        else
        {
            if(transform.position.x > 0)
            {
                transform.position += Vector3.right * 0.05f;
            }
            else
            {
                transform.position += Vector3.left * 0.05f;
            }
        }
    }   
  • 역시 0.05를 곱해 속도를 늦춰주자.
  • 이제 마지막으로 고양이가 처음 생성됐을 때 랜덤한 위치에 세팅이 되어야 한다.
  • 위치는 화면 밖(y 30)에서부터 랜덤하게(x -9, 9)떨어지게 세팅해 주자.
    void Start()
    {
        Application.targetFrameRate = 60;
        float x = Random.Range(-9.0f, 9.0f);
        float y = 30.0f;
        transform.position = new Vector2(x, y);
    }
  • x값은 Random.Range로 랜덤하게 추출하고, y값은 30으로 고정한다. (나중에 y값을 수정할 수도 있으니 y도 변수에 담아줬다.)
  • 플레이 해보면 고양이가 랜덤하게 나타나는데, 게이지가 다 차도 바로 FullCat으로 바뀌지 않는다.
private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            if(energy < full)
            {
                energy += 1.0f;
                front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                Destroy(collision.gameObject);
            }
            else
            {
                HungryCat.SetActive(false);
                FullCat.SetActive(true);
            }
        }
    }
  • 이 부분이 문제인데, 만약 energy가 4고, full이 5인 상태라면 4에서 5가 되면서 우리에게는 HPBar가 꽉 차게 보인다.
  • 그런데 HungryCat과 fullCat을 스위치 시켜주는 타이밍이 5가 된 다음에 충돌했을 때 else문을 타게 되니 딜레이가 생긴 것. 이 부분을 수정해 주자.
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            if(energy < full)
            {
                energy += 1.0f;
                front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                Destroy(collision.gameObject);
                if(energy == 5.0f)
                {
                    HungryCat.SetActive(false);
                    FullCat.SetActive(true);
                }
            }
        }
    }
  • 이렇게 else가 아닌 energy가 5일 때(if(energy == 5.0f))로 바꿔주면, 플레이 했을 때 게이지가 차고 나서 바로 FullCat으로 바뀌는 것을 볼 수 있다.

고양이 만들기 - 반복 생성 로직(3-5)

고양이 반복적으로 생성하기

  • 이제 고양이 오브젝트를 반복적으로 생성해 주자.
  • GameManager 오브젝트와 스크립트를 만들어 주자.
public class GameManager : MonoBehaviour
{
    public GameObject normalCat;

    void Start()
    {

    }

    void Update()
    {

    }

    void MakeCat()
    {
        Instantiate(normalCat);
    }
}
  • 게임 오브젝트를 생성해 주는 MakeCat 함수를 만들고 normalCat을 public 했다.
  • normalCat을 반복적으로 생성하기 위해 Instantiate 안에 normalCat을 넣었다.
public class GameManager : MonoBehaviour
{
    public GameObject normalCat;

    void Start()
    {
        InvokeRepeating("MakeCat", 0f, 1f);
    }

    void Update()
    {

    }

    void MakeCat()
    {
        Instantiate(normalCat);
    }
}
  • 그 다음 Start에 InvokeRepeating 함수를 만들었다. 즉시, 1초마다 생성하게 할 거니 0f, 1f.
  • GameManager에 스크립트를 추가해 주고, NormalCat 변수에도 Prefab 적용
  • 플레이 해보면, 반복적으로 NormalCat이 생성되는 것을 볼 수 있다.

Retry 버튼

  • 이제 고양이가 생선 가게까지 내려왔을 때 게임 오버 해 주는 Retry 버튼을 띄워주자.
  • UI -> Legacy -> Button 추가 -> retryBtn 이미지 적용
  • 버튼을 눌렀을 때 게임을 다시 시작하게 하려면 MainScene을 다시 불러오면 되는데, MainScene을 불러오는 로직은 StartScene을 만들 때 이미 만들어 놨다.
  • 버튼에 StartButton 스크립트를 적용한 다음, On Click에 버튼을 넣고 StartGame()을 등록하자.
  • 이제 고양이가 생선 가게까지 내려왔을 때 이 리플레이 버튼을 띄워주는 로직도 만들자.
  • 일단 Canvas 오브젝트는 꺼주자.
    public void GameOver()
    {

    }
  • 일단 GameOver를 만들었다. 그리고 게임이 종료됐을 때 RetryButton을 켜주면 되겠다.
    public GameObject retryBtn;

    public void GameOver()
    {

    }
  • 그러기 위해 retryBtn도 생성
  • 이걸 게임 오버됐을 때 켜주면 된다.
    public void GameOver()
    {
        retryBtn.SetActive(true);
    }
  • 켜줌
  • 이 게임 오버 함수를 고양이가 생선 가게까지 내려왔을 때 켜주면 될 것 같다.
  • 그러기 위해서는 GameManager를 외부에서 접근할 수 있게 싱글톤으로 만들어 주자.
public class GameManager : MonoBehaviour
{
    public static GameManager Instance;

    public GameObject normalCat;
    public GameObject retryBtn;

    private void Awake()
    {

    }
  • static을 추가해 줬다. 그리고 Awake는 Start보다 위에 만들어 준다.
    private void Awake()
    {
        if(Instance == null)
        {
            Instance = this;
        }
    }
  • Instance라는 함수가 비어있을 때(null) 이 변수에다가 자기 자신을 넣어 주자.
  • 이렇게 싱글톤까지 만들었다.
  • 이제 고양이가 일정한 위치(y -16)까지 내려왔을 때 GameManager에 있는 GameOver를 호출해주면 되겠다.
    void Update()
    {
        if(energy < full)
        {
            transform.position += Vector3.down * 0.05f;
        }
    }   
  • Cat 스크립트로 돌아왔다.
  • 여기서 Y가 -16보다 작아졌을 때 GameOver를 호출해주면 되겠다. 조건문을 하나 더 달자.
    void Update()
    {
        if(energy < full)
        {
            transform.position += Vector3.down * 0.05f;
            if(transform.position.y < -16.0f)
            {
                GameManager.Instance.GameOver();
            }
        }
  • Position의 y값이 -16보다 작아졌을 때 게임 매니저에 있는 GameOver를 켜준다.
    private void Awake()
    {
        if(Instance == null)
        {
            Instance = this;
        }
        Application.targetFrameRate = 60;
    }
  • 그리고 전에 만들어 놨던 targetFrameRate는 게임 메니저로 옮겨주자.
  • GameManager의 Awake 안에 넣어줬다.
  • 유니티로 돌아와서 GameManager Retry Btn 변수에 Canvas를 넣어주고 플레이 해보면, 고양이가 생선 가게에 닿았을 때 리플레이 버튼이 뜨고, 버튼을 누르면 다시 MainScene으로 돌아와서 게임이 다시 시작되는 걸 볼 수 있다.
  • 그런데 게임이 끝났는데도 밥은 계속 나가고 고양이도 계속 생성되고 있다.
  • 리플레이 버튼이 나오면 게임을 정지해주는 로직까지 만들어 보자.
    public void GameOver()
    {
        retryBtn.SetActive(true);
        Time.timeScale = 0f;
    }
  • 게임 오버가 됐을 때 timeScale을 0으로 만들어 주는 로직 작성
    private void Awake()
    {
        if(Instance == null)
        {
            Instance = this;
        }
        Application.targetFrameRate = 60;
        Time.timeScale = 1.0f;
    }
  • 게임이 시작됐을 때 timeScale을 1로 만들어 주는 로직 작성
  • 플레이 해보면, 게임이 종료됐을 때 밥도 멈추고 고양이 생성도 멈춘다.
  • 근데~~ 딱 하나만 더 하자. 게이지가 다 찬 고양이들은 파괴시켜 주자.
private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.CompareTag("Food"))
        {
            if(energy < full)
            {
                energy += 1.0f;
                front.localScale = new Vector3(energy / full, 1.0f, 1.0f);
                Destroy(collision.gameObject);
                if(energy == 5.0f)
                {
                    HungryCat.SetActive(false);
                    FullCat.SetActive(true);
                    Destroy(gameObject, 3.0f);
                }
            }
        }
    }
  • energy가 5가 된 고양이는 Destroy 시켜주되 3초 정도 뒤에 파괴될 수 있게 했다.
  • 플레이 해보면, 밥을 다 먹은 고양이들은 왼쪽 혹은 오른쪽으로 이동하다가 사라지는 것을 볼 수 있다.