고양이 밥주기 게임
게임 설명
강아지의 생선가게에 자꾸 배고픈 고양이들이 와서 생선을 훔쳐간다고 한다. 강아지는 생선가게까지 오기 전에 밥을 줘서 생선을 지키기로 했다. 레벨이 오를수록 다양한 고양이가 찾아온다.
달려오는 고양이에게 밥을 먹여 생선을 지키자!
플레이 영상
학습 목표
1. 그럴싸한 게임을 완성해보기
2. HP바 만들기
3. 레벨 시스템을 구상해보기
목차
0. 기본 씬 구성하기
1. 강아지 만들기 - 밥 쏘기
2. 고양이 만들기 - 고양이와 배부른 고양이
3. 고양이 만들기 - 중력, HP바
4. 고양이 만들기 - 반복 생성
5. 레벨 구성하기 - 레벨업 표시하기
6. 게임 끝내기 - 끝내기 / 다시 시작
0. 기본 씬 구성하기
UnityPackage로 에셋 Import
미리 받아둔 UnityPackage를 에디터의 Asset 폴더에 끌어 넣으면 아래와 같이 창이 뜬다.
Import 를 클릭하면 Assetss 폴더 아래에 Fonts와 Images 가 잘 임포트된 것을 볼 수 있다.
시작화면 만들기
StartScene의 배경과 시작 버튼 UI를 구성한다.
시작 버튼 UI에 Button 컴포넌트를 추가하고 OnClick() 리스트에 StartButton 스크립트의 StartGame() 함수를 추가한다.
public class StartButton : MonoBehaviour
{
public void StartGame()
{
SceneManager.LoadScene("MainScene");
}
}
1. 강아지 만들기 - 밥 쏘기
밥 만들어두기
Food 프리팹을 만들어뒀다. Tag도 Food를 만들어 달아줬다.
가장 중요한 것은 Rigidbody 2D의 Body Type 옵션을 Kinematic으로 변경해야 한다는 것이다.
Rigidbody 2D 컴포넌트에서 Body Type 옵션은 물리 시뮬레이션에서 오브젝트가 어떻게 동작할지를 정의한다.
- Dynamic : 물리 법칙(중력, 충돌, 힘)의 영향을 받는 설정.
- Kinematic : 물리 엔진의 영향을 받지 않고 직접적인 코드로 이동을 제어하는 설정. (충돌은 감지되지만 그에 의한 물리 계산이 적용되지 않음)
- Static : 이동하지 않는 오브젝트(예시: 벽, 플랫폼)에 적합. 물리에진의 영향을 받지 않고 충돌만 감지.
근데 Kinematic으로 설정하면 OnCollisionEnter도 동작하지 않게 된다. 그래서
Collider에 있는 IsTrigger 옵션을 켜주고, OnTriggerEnter2D로 충돌처리를 해줘야 한다.
강아지 움직임 & 밥 쏘기
- Start() 에서 MakeFood 함수를 연속 실행한다.
- MakeFood() 에서 Food를 생성한다.
- Update() 에서 강아지가 x 위치만 마우스를 따라다니도록 이동시킨다.
using UnityEngine;
public class Dog : MonoBehaviour
{
public GameObject food;
void Start()
{
InvokeRepeating("MakeFood", 0f, 0.2f);
}
private void Update()
{
// Dog의 위치는 y는 고정, x는 마우스를 따라감
Vector2 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
float x = mousePos.x;
// 일정 거리 이상 나가지 않도록 함
if(x > 8.5f)
{
x = 8.5f;
}
if (x < -8.5f)
{
x = -8.5f;
}
transform.position = new Vector2(x, transform.position.y);
}
void MakeFood()
{
float x = transform.position.x;
float y = transform.position.y + 2;
Instantiate(food, new Vector2(x, y), Quaternion.identity); // Quaternion.identity << 별도의 회전값을 넣지 않겠다는 뜻. food 그대로 회전값 없이 생성하겠다는 뜻.
}
}
강아지와 생선가게가 위로 오게 만들기
고양이가 강아지와 생선가게보다 위로 지나가지 않게 하기 위해 Dog와 FishShop의 Sprite Renderer > Order in Layer를 1로 조정한다.
2. 고양이 만들기 - 고양이와 배부른 고양이
평범한 고양이 Normal Cat 만들기
Create Empty 로 NormalCat을 만들고, 그 아래에 두 개의 Square 2D 오브젝트를 만든다. 각각 Hungry 상태의 고양이와 먹이를 다 먹은 Full 상태의 고양이를 표현한다. Hungry 고양이로 생성되고서 먹이를 다 먹으면 Hungry를 끄고 Full 고양이를 켤 것이다.
Hungry 하위에는 Image UI를 두 개 만든다. (Back, Front)
NormalCat의 Hungry에 애니메이션을 붙여준다.
뚱뚱한 고양이 Fat Cat 만들기
레벨 3에 등장하게 될, 밥을 더 많이 먹고 천천히 움직이는 뚱뚱한 고양이다.
NormalCat을 복사하고 Hungry, Full의 이미지를 뚱뚱한 고양이로 교체해준다.
FatCat의 Hungry에도 애니메이션을 붙여준다.
해적 고양이 Pirate Cat 만들기
레벨이 4 이상이 되면 등장하는 해적 고양이다.
FatCat과 마찬가지로 복사하고서 Hungry, Fat 이미지를 교체하고 PirateCat 이미지의 애니메이션을 붙인다.
각각의 고양이들의 특징 (속도, 밥 먹는 양)을 Start 함수에서 설정한다.
// Cat.cs
void Start()
{
float x = Random.Range(-9.0f, 9.0f);
float y = 30.0f;
transform.position = new Vector2(x, y);
if (type == 1) // Normal Cat
{
speed = 0.05f;
full = 5f;
}
else if(type == 2) // Fat Cat
{
speed = 0.02f;
full = 10f;
}
else if (type == 3) // Pirate Cat
{
speed = 0.1f;
full = 5f;
}
}
NormalCat, FatCat과 PirateCat 모두 프리팹화 해두기.
3. 고양이 만들기 - 중력, HP바
고양이 내려오게 하기
- Update()에서 아직 배부르지 않다면 Vector3.down * speed 만큼씩 내려오게 했다.배부르면 왼쪽이나 오른쪽으로 사라지도록 했다.
특정 위치 이하로 내려가면 GameOver() 실행한다.
// Cat.cs
void Update()
{
if (energy < full)
{
transform.position += Vector3.down * speed;
if(transform.position.y < -16.0f)
{
GameManager.instance.GameOver();
}
}
else
{
if(transform.position.x > 0)
{
transform.position += Vector3.right * 0.05f;
}
else
{
transform.position += Vector3.left * 0.05f;
}
}
}
고양이와 밥 충돌하게 하기 & HP바 채우기
충돌한 물체가 Food 태그라면 에너지를 채워준다.
HP바에서 HP를 표시하는 front의 scale을 조절해서 HP바를 채운다. front.localScale = new Vector3(energy / full, 1f, 1f);
front UI의 pivot은 X: 0, Y: 0.5 이다.
고양이의 에너지가 꽉 차게 되면 Hungry를 비활성화하고 Full을 활성화한다.
// Cat.cs
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.CompareTag("Food"))
{
if(energy < full)
{
energy += 1.0f; // 에너지 채우기
front.localScale = new Vector3(energy / full, 1f, 1f); // HP바 채우기
Destroy(collision.gameObject);
if(energy == full) // energy full됐을 때 바로 fullCat 보여줘야 함.
{
if(!isFull)
{
isFull = true;
hungryCat.SetActive(false);
fullCat.SetActive(true);
Destroy(gameObject, 3.0f);
GameManager.instance.AddScore();
}
}
}
}
}
Cat.cs 전체
using UnityEngine;
public class Cat : MonoBehaviour
{
public GameObject hungryCat;
public GameObject fullCat;
public RectTransform front;
public int type; // 1일때 NormalCat, 2일때 FatCat, 3일때 PirateCat
float full = 5.0f;
float energy = 0.0f;
float speed = 0.5f;
bool isFull = false;
void Start()
{
float x = Random.Range(-9.0f, 9.0f);
float y = 30.0f;
transform.position = new Vector2(x, y);
if (type == 1) // Normal Cat
{
speed = 0.05f;
full = 5f;
}
else if(type == 2) // Fat Cat
{
speed = 0.02f;
full = 10f;
}
else if (type == 3) // Pirate Cat
{
speed = 0.1f;
full = 5f;
}
}
void Update()
{
if (energy < full)
{
transform.position += Vector3.down * speed;
if(transform.position.y < -16.0f)
{
GameManager.instance.GameOver();
}
}
else
{
if(transform.position.x > 0)
{
transform.position += Vector3.right * 0.05f;
}
else
{
transform.position += Vector3.left * 0.05f;
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.CompareTag("Food"))
{
if(energy < full)
{
energy += 1.0f; // 에너지 채우기
front.localScale = new Vector3(energy / full, 1f, 1f); // HP바 채우기
Destroy(collision.gameObject);
if(energy == full) // energy full됐을 때 바로 fullCat 보여줘야 함.
{
if(!isFull)
{
isFull = true;
hungryCat.SetActive(false);
fullCat.SetActive(true);
Destroy(gameObject, 3.0f);
GameManager.instance.AddScore();
}
}
}
}
}
}
4. 고양이 만들기 - 반복 생성
전반적인 게임 로직을 관여하는 GameManager를 만들고, Start 함수에서 MakeCat() 를 연속 실행했다.
MakeCat() 에서는 기본적으로 normalCat을 생성하고, 레벨에 따라 다음과 같은 규칙으로 고양이를 반복 생성한다.
lv.1 20% 확률로 고양이를 더 생성
lv.2 50% 확률로 고양이를 더 생성
lv.3 뚱뚱한 고양이를 50% 확률로 생성
lv.4 이후로 해적 고양이를 50% 확률로 생성
// GameManager.cs
void Start()
{
InvokeRepeating("MakeCat", 0.0f, 1.0f);
}
void MakeCat()
{
Instantiate(normalCat);
// lv.1 20% 확률로 고양이를 더 생성
// lv.2 50% 확률로 고양이를 더 생성
// lv.3 뚱뚱한 고양이를 50% 확률로 생성
// lv.4 이후로 해적 고양이를 50% 확률로 생성
if (level == 1)
{
int p = Random.Range(0, 10);
if(p < 2) Instantiate(normalCat);
}
else if(level == 2)
{
int p = Random.Range(0, 10);
if (p < 5) Instantiate(normalCat);
}
else if(level == 3)
{
int p = Random.Range(0, 10);
if (p < 5) Instantiate(fatCat); // 50% 확률로 fatCat 생성으로 바꿈
}
else if (level >= 4)
{
Instantiate(pirateCat);
}
}
5. 레벨 구성하기 - 레벨업 표시하기
UI로 레벨과 경험치 바를 표현한다. 경험치늰 HP바와 동일하게 Front의 scale을 조절해서 표현한다.
고양이가 full 상태가 되는 순간 GameManager.instance.AddScore()를 실행한다.
score 변수를 높이고 level은 score를 5로 나눈 몫으로 표시한다. 레벨업을 위한 경험치가 총 5라는 의미가 된다.
// GameManager.cs
public void AddScore()
{
score++;
level = score / 5;
levelTxt.text = level.ToString();
levelFront.localScale = new Vector3((score - level * 5) / 5.0f, 1f, 1f);
}
6. 게임 끝내기 - 끝내기 / 다시 시작
Cat.cs 에서 고양이가 특정 위치 아래로 내려오면 GameManager.instance.GameOver()를 실행했다.
다시 시작 버튼 UI를 활성화하고, timeScale을 0으로 만들어 게임 진행을 멈추게 한다.
// GameManager.cs
public void GameOver()
{
retryBtn.SetActive(true);
Time.timeScale = 0.0f;
}
다시 시작 버튼에는 Button 컴포넌트를 넣어주고, OnClick() 리스트에 StartButton 스크립트의 StartGame함수를 재활용한다.
GameManager.cs 전체
using UnityEngine;
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
public static GameManager instance;
public GameObject normalCat;
public GameObject fatCat;
public GameObject pirateCat;
public GameObject retryBtn;
public RectTransform levelFront;
public Text levelTxt;
int level = 0;
int score = 0;
private void Awake()
{
if(instance == null)
{
instance = this;
}
Application.targetFrameRate = 60;
Time.timeScale = 1.0f;
}
void Start()
{
InvokeRepeating("MakeCat", 0.0f, 1.0f);
}
void MakeCat()
{
Instantiate(normalCat);
// lv.1 20% 확률로 고양이를 더 생성
// lv.2 50% 확률로 고양이를 더 생성
// lv.3 뚱뚱한 고양이를 50% 확률로 생성
// lv.4 이후로 해적 고양이를 50% 확률로 생성
if (level == 1)
{
int p = Random.Range(0, 10);
if(p < 2) Instantiate(normalCat);
}
else if(level == 2)
{
int p = Random.Range(0, 10);
if (p < 5) Instantiate(normalCat);
}
else if(level == 3)
{
int p = Random.Range(0, 10);
if (p < 5) Instantiate(fatCat); // 50% 확률로 fatCat 생성으로 바꿈
}
else if (level >= 4)
{
Instantiate(pirateCat);
}
}
public void GameOver()
{
retryBtn.SetActive(true);
Time.timeScale = 0.0f;
}
public void AddScore()
{
score++;
level = score / 5;
levelTxt.text = level.ToString();
levelFront.localScale = new Vector3((score - level * 5) / 5.0f, 1f, 1f);
}
}
'공부 기록 > 유니티 Unity' 카테고리의 다른 글
[Unity/TIL] 유니티 개발 입문 (이미지 여러 개 있는 리소스 편집법, 라이프사이클, Rigidbody, GetComponent, Collision) (0) | 2025.02.14 |
---|---|
[Unity] "르탄이 카드 뒤집기" 2D 게임 만들기 (1) (0) | 2025.01.17 |
[Unity] "풍선을 지켜라" 2D 게임 만들기 (0) | 2025.01.13 |
[Unity] "빗물받는 르탄이" 2D 게임 만들기 (1) | 2025.01.10 |
[유니티(Unity)] 에셋 스토어에서 무료 에셋 구입해보기 (2) | 2024.12.31 |