개임 게발
[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초 정도 뒤에 파괴될 수 있게 했다.
- 플레이 해보면, 밥을 다 먹은 고양이들은 왼쪽 혹은 오른쪽으로 이동하다가 사라지는 것을 볼 수 있다.
'강의 정리' 카테고리의 다른 글
[Unity] [르탄이 카드 뒤집기] 카드 게임 구성하기, 카드 배치하기 (0) | 2025.02.03 |
---|---|
[Unity] [고양이 밥주기] Level 로직과 Level 시스템 만들기 (1) | 2025.02.02 |
[Unity] [고양이 밥주기] StartScene 구성하기, 마우스를 따라 밥 쏘게 만들기 (0) | 2025.02.02 |
[Unity] [풍선을 지켜라] 게임 종료 로직, 최고 점수 로직 만들기 (0) | 2025.02.02 |
[Unity] [풍선을 지켜라] 풍선과 장애물 만들기, 타이머 만들기 (0) | 2025.02.02 |