본문 바로가기
Development/멋쟁이사자처럼 게임개발 부트캠프

[멋쟁이사자처럼 Unity 게임 부트캠프 4기] 13일차 - Dragon Flight (2)

by jjeondeuk1008 2025. 3. 19.
반응형

 

[ 목차 ]


     

    오늘은 드래곤 플라이트 끝까지 만들기!

     

    유명 게임을 재구현 해보니까

    다시 느껴지는 게임의 재미 ㅎㅎ 개발의 재미

     

    끝까지 달려봅시다 파이팅 파이팅!

     


     

     

    1. 적 추가하기

     

    이제 플레이어을 추가 했으니 공격할 적을 추가할 것이다.

     

    적으로 쓸 이미지를 Hierarchy에 추가하고, Inspector에서 Circle Collider 2D를 추가한다.

    플레이어와 동일하게 콜라이더 범위를 알아서 조정한다.

     

     

     

     

    적의 스크립트를 생성한다.

    적이 움직일 속도를 지정하고, -Y 값을 받아야 한다. (위에서 아래로)

    화면 밖으로 나가면 카메라에서 보이지 않는 호출도 추가한다.

    using UnityEngine;
    
    public class Enemy : MonoBehaviour
    {
        //움직일 속도 지정
        public float moveSpeed = 1.3f;
    
        void Start()
        {
            
        }
    
        void Update()
        {
            //움직일 거리를 계산
            float distanceY = moveSpeed * Time.deltaTime;
    
            //움직임을 반영
            transform.Translate(0, -distanceY, 0);
            
        }
    
        //화면 밖으로 나가 카메라에서 보이지 않으면 호출
        private void OnBecameInvisible()
        {
            Destroy(gameObject); //객체 삭제
        }
    }
    
    

     

    여기에서 주의점을 말하자면, Game 뷰에서만 벗어나면 없어지는 것이 아니라

    Scene에서의 화면 밖으로 나가야 없어지는 것이다…

    Scene을 보고 있는 모습도 카메라이다…!

     

     

     

    충돌 처리를 위해

    적의 inspector에서 Circle Collider 2D의 is Trigger 체크를 한다.

     

     

     

     

     


     

    2. Prefabs 만들기

     

     

    동일한 오브젝트를 복제하는 것이 아니라, 하나의 Prefabs를 만드는 것이다.

    Prefabs 폴더를 만든 후에 Hierarchy의 오브젝트를 Prefabs 폴더 안에 드래그를 해 생성

     

     

     

    짜잔~ 파란색의 오브젝트로 변경이 되었다.

    이것이 프리팹이 되었다는 증거이다.

     

    간단하죠!

     

     

     

     


     

    3. 미사일 생성

     

     

    이제 플레이어에서 나갈 미사일을 만들 것이다.

     

    Box Collider 2D 생성 및 크기 조정

    Rigidbody2D 생성 후 중력 0으로 수정

     

     

     

     

     

    적과 비슷한 스크립트 생성한다.

     

    대문자 GameObject는 클래스,

    gameObject는 나 자신(이 스크립트를 가지고 있는 오브젝트)

    using UnityEngine;
    
    public class Bullet : MonoBehaviour
    {
        public float moveSpeed = 0.45f;
    
        void Start()
        {
            
        }
    
        void Update()
        {
            //Y축 이동
            transform.Translate(0, moveSpeed * Time.deltaTime, 0);
            
        }
    
        private void OnBecameInvisible()
        {
            //미사일이 화면 밖으로 나가면 미사일 지우기
            Destroy(gameObject);
    
            
        }
    
    }
    
    

     

     

     

     

    적과 미사일이 충돌하기 위해 Is Trigger 설정

     

     

     

     

     

    미사일도 여러개 만들 것이기 때문에

    적과 같은 방식으로 Prefabs 생성

     

     

     

    Prefabs를 만든 후 Hierarchy에 있던 bullet를 삭제한다. (Prefabs 폴더에선 없어지지 않음)

    Create Empty를 만든 후에 launcher로 이름을 변경하였다.

     

     

     

     

    미사일이 활성화 되지 않았을 때는 아예 모습을 볼 수 없으니

    Inspector에서 표시할 모습을 변경한다.

     

     

     

     

    미사일이 생성될 곳 위치 조정

     

     

     

     

     

    이제 미사일 스크립트를 생성한다.

    using UnityEngine;
    
    public class launcher : MonoBehaviour
    {
        public GameObject bullet; //미사일 프리팹 가져올 변수
    
        void Start()
        {
            //InvokeRepeating(함수이름, 초기지연시간, 지연할 시간)
            InvokeRepeating("Shoot", 0.5f, 0.5f); //0.5초 쉬고, 0.5초마다 호출
        }
    
        void Shoot()
        {
            //미사일 프리팹, 런쳐 포지션, 방향값 안줌
            Instantiate(bullet, transform.position, Quaternion.identity);
        }
    
        void Update()
        {
            
        }
    }
    
    

     

     

     

    이 스크립트를 다 입력하면

    launcher 스크립트에 bullet 넣을 곳이 생긴다.

    아까 만들어뒀던 bullet prefabs를 저곳에 드래그로 넣어준다.

     

     

     

    이제 Tag를 새로 생성할 것이다. Inspector에 보면 Tag가 현재 Untagged인데 클릭해 Add Tag...로 들어간다.

     

     

     

     

    새로운 Enemy 이름의 Tag를 만들고 저장한다.

     

     

     

    새로 만든 Enemy 태그로 변경한다.

     

     

     

    bullet 스크립트에 충돌 이벤트를 추가한다.

    Tag가 Enemy인 적을 탐지해낸단 것이다.

    //2D 충돌 트리거 이벤트
    private void OnTriggerEnter2D(Collider2D collision)
    {
        //미사일과 적이 부딪히다!
        if(collision.gameObject.tag == "Enemy")
        {
            //적 지우기
            Destroy(collision.gameObject);
    
            //미사일 지우기
            Destroy(gameObject);
        }
    }
    

     

     

    OnCollisionEnter 2D

    → trigger 체크 안 된 상태 충돌

     

    OntriggerEnter 2D

    → trigger 체크된 상태 충돌

     

    stay

    충돌이 머무를 때

     

    exit

    충돌에서 벗어날 때

     

     

     

     


     

    4. Asset Store 적용 및 이펙트 적용

     

     

     

    (1) Asset Store

     

    필요한 에셋을 찾기 위해 유니티에서 다운로드할 수 있는 Asset Store에서 찾을 것이다.

     

    window → Asset Store 클릭

     

     

     

    난 여기에서 찾는게 VFX이다.

     

    그래서 원하는 무료 VFX 에셋을 추가한 뒤에 Unity에서 열기를 선택한다.

     

     

     

    유니티에서 열기를 눌러보면

    Package Manager가 열릴 것이다.

    거기에서 에셋을 다운로드 받는다.

     

     

     

    다운로드 후 Import를 누르면 된다.

    Import과 완료되면 Project 창에 에셋 폴더가 생긴다.

     

     

     

     

     

    (2) 이펙트(VFX) 적용

     

     

    폴더 안에 프리팹을 찾아 원하는 것을 Hierarchy에 드래그해 사용한다.

     

     

     

    기존 Bullet 스크립트에 이 코드를 추가한다.

    public GameObject expostion;
    

     

     

     

    Bullet 오브젝트 Inspector를 보면 스크립트에 Expostion 넣는 곳이 보인다.

     

     

     

    에셋의 Prefab으로 가져왔던 VFX를 새로운 Prefab로 만들어

    Bullet의 Expostion 자리에 이 Prefab을 넣어준다.

     

     

     

     

    기존 Bullet 스크립트로 다시 돌아가 조건문을 수정하고 폭발 이펙트를 생성한다.

    //미사일과 적이 부딪히다!
    //if(collision.gameObject.tag == "Enemy")
    if (collision.gameObject.CompareTag("Enemy"))
    {
        //폭발 이펙트 생성
        Instantiate(expostion, transform.position, Quaternion.identity);
        
        //적 지우기
        Destroy(collision.gameObject);
    
        //미사일 지우기
        Destroy(gameObject);
    }
    

     

     

     

    그렇게 된 Bullet의 전체 코드이다.

    using UnityEngine;
    
    public class Bullet : MonoBehaviour
    {
        public float moveSpeed = 0.45f;
        public GameObject expostion;
    
        void Start()
        {
            
        }
    
        void Update()
        {
            //Y축 이동
            transform.Translate(0, moveSpeed * Time.deltaTime, 0);
            
        }
    
        private void OnBecameInvisible()
        {
            //미사일이 화면 밖으로 나가면 미사일 지우기
            Destroy(gameObject);
    
            
        }
    
        //2D 충돌 트리거 이벤트
        private void OnTriggerEnter2D(Collider2D collision)
        {
            //미사일과 적이 부딪히다!
            //if(collision.gameObject.tag == "Enemy")
            if (collision.gameObject.CompareTag("Enemy"))
            {
                //폭발 이펙트 생성
                Instantiate(expostion, transform.position, Quaternion.identity);
                
                //적 지우기
                Destroy(collision.gameObject);
    
                //미사일 지우기
                Destroy(gameObject);
            }
        }
    
    }
    
    

     

     

     

    플레이를 해볼 때 이펙트가 즉시 생성되는 걸 수도 있다.

    Hierarchy에서 VFX 오브젝트를 없애준다.

     

     

     

     


     

    5. Singleton

     

     

    Hierarchy에 Singleton 생성

     

     

     

     

    Singleton 스크립트 생성

    using UnityEngine;
    
    public class Singleton : MonoBehaviour
    {
        //Singleton : 유니티에서 하나의 인스턴스만 유지하면서 어디서든 접근 가능
        public static Singleton Instance { get; private set; }
    
        //Start보다 더 빠른 Awake, 기능은 Start와 동일(한 번 실행함)
        private void Awake()
        {
            if(Instance == null)
            {
                Instance = this;
                DontDestroyOnLoad(gameObject); //Scene이 바뀌어도 유지가 됨
            }
            else
            {
                Destroy(gameObject); //중복 생성 방지
            }
        }
    
        public void PrintMessage()
        {
            Debug.Log("싱글톤 메시지 출력!");
        }
    }
    
    

     

     

     

     

    Bullet 스크립트로 돌아가 수정할 것이다.

    Singleton 함수 가져오기 코드를 붙여 넣는다.

     void Start()
     {
         Singleton.Instance.PrintMessage();
     }
    

    이 코드는 확인용으로 한 뒤에 주석 처리로 변경해야 할 것이다.

     

     

     

     

    미사일이 발사될 때마다 첫 실행되는 싱글톤 메시지 출력이다.

     

     

     

     

    Prefab에 있는 enemy는 Tag가 설정되어 있지 않고 Hierarchy에 있는 enemy는 Tag가 Enemy로 되어 있을 수 있다.

    이럴 경우엔 Hierarchy에 있는 오브젝트를 드래그해 기본 Frefab 위에 덮어씌우기를 하면

    Hierarchy에 있는 Inspector를 따라간다. (Tag - Enemy로 변경됨)

     

     

     

     

    Empty를 생성해 SpawnManager으로 이름을 바꾸고 Enemy가 소환할 곳 위치를 지정한다.

     

     

     

     

    이제 Sqawn 스크립트를 작성할 것이다.

    using UnityEngine;
    
    public class SpawnManager : MonoBehaviour
    {
        //적 가지고 오기
        public GameObject enemy;
    
        void SpawnEnemy()
        {
            float randomX = Random.Range(-2f, 2f); //적이 나타날 z좦표를 랜덤으로 생성 -2 ~ 2
            //적을 생성한다. randomX 랜덤한 x값
            //Vector3(랜덤한 x값, position Y값, z는 0)
            Instantiate(enemy, new Vector3(randomX, transform.position.y, 0f), Quaternion.identity);
        }
        void Start()
        {
            //Spawn
            InvokeRepeating("SpawnEnemy", 1, 0.5f);
        }
    
        void Update()
        {
            
        }
    }
    
    

     

     

     

     

    Enemy에 기존 enemy prefab을 넣어준다.

     

     

     

     


     

    6. 사운드 매니저

     

     

    Empty를 추가해 SoundManager 이름으로 변경 후

    Inspector에 Audio Source를 추가한다.

     

     

     

     

    SoundManager 스크립트를 작성할 것이다.

    using UnityEngine;
    
    public class SoundManager : MonoBehaviour
    {
        public static SoundManager instance; //자기자신을 변수로 담기
    
        AudioSource myAudio; //AudioSource 컴포넌트를 변수로 담는다.
    
        public AudioClip SoundBullet; //재생할소리
        public AudioClip soundDie; //죽는 사운드
        private void Awake()
        {
            if (SoundManager.instance == null)  //인스턴스있는지 검사
            {
                SoundManager.instance = this; //자기자신을 담는다.
            }
        }
     
        void Start()
        {
            myAudio = GetComponent<AudioSource>(); //AudioSource 컴포넌트 가져오기
        }
    
        public void PlayBulletSound()
        {
            myAudio.PlayOneShot(SoundBullet);
        }
    
        public void SoundDie()
        {
            myAudio.PlayOneShot(soundDie); //몬스터 죽음사운드
        }
    }
    
    

     

     

     

    bullet 미사일 스크립트로 가서 수정한다.

    죽음 사운드를 생성하는 것이다.

    //2D 충돌 트리거 이벤트
    private void OnTriggerEnter2D(Collider2D collision)
    {
        //미사일과 적이 부딪히다!
        //if(collision.gameObject.tag == "Enemy")
        if (collision.gameObject.CompareTag("Enemy"))
        {
            //폭발 이펙트 생성
            Instantiate(expostion, transform.position, Quaternion.identity);
    
            //죽음 사운드
            SoundManager.instance.SoundDie();
            
            //적 지우기
            Destroy(collision.gameObject);
    
            //미사일 지우기
            Destroy(gameObject);
        }
    }
    

     

     

     

     

    SoundManager에 Sound Bullet, Sound Die 두 가지의 음악을 넣을 것

     

     

     

     


     

    7. UI 추가하기

     

     

     

    Legacy Text 생성하기

    Hierarchy - UI - Legacy - Text

     

     

     

    Text를 더블 클릭하면 UI 화면 창으로 나온다.

     

     

     

     

    Text Inspector 창에서

    우선 화면에 보이고 싶어 하는 Text를 입력한다.

     

    Font Size를 통해 글씨의 크기를 조절하고

    글씨의 크기에 비해 위의 Width Height가 적으면 보이지 않을 수 있으니 넉넉하게 늘려주길 바란다.

     

    Alignment로 정렬을 원하는 데로 맞추고,

    컬러도 취향껏 선정하면 된다.

     

     

     

     

    Rect Transform 에서 기준점을 정할 수 있는데,

    보통 오른쪽 위에 배치할 생각이라면 top / right를 선택해

    기준점이 오른쪽 위 모서리로 향하도록 한다.

     

     

     

     

    GameManager 생성한다.

     

     

     

     

     

    GameManager 스크립트를 작성한다.

    점수 Score를 작성하는 것이다.

    using UnityEngine;
    using UnityEngine.UI;
    
    public class GameManager : MonoBehaviour
    {
        //싱글톤 : 디자인 패턴
        public static GameManager intance;
        public Text scoreText; //점수를 표시하는 Text 객체를 에디터에서 받아옴
    
        int score = 0; //점수를 관리한다.
    
        private void Awake()
        {
            if(intance == null) //정적으로 자신을 체크한다. null인지
            {
                intance = this; //자기 자신을 저장한다.
            }
        }
    
        void Start()
        {
            
        }
        public void AddScore(int num)
        {
            score += num; //점수를 더해준다.
            scoreText.text = "Score: " + score; //텍스트에 반영한다.
        }
    }
    
    

     

     

     

     

     

    Bullet 스크립트 수정한다.

    점수를 올려주는 스크립트를 추가하는 것이다.

    //2D 충돌 트리거 이벤트
    private void OnTriggerEnter2D(Collider2D collision)
    {
        //미사일과 적이 부딪히다!
        //if(collision.gameObject.tag == "Enemy")
        if (collision.gameObject.CompareTag("Enemy"))
        {
            //폭발 이펙트 생성
            Instantiate(expostion, transform.position, Quaternion.identity);
    
            //죽음 사운드
            SoundManager.instance.SoundDie();
    
            //점수 올려주기
            GameManager.intance.AddScore(10);
            
            //적 지우기
            Destroy(collision.gameObject);
    
            //미사일 지우기
            Destroy(gameObject);
        }
    }
    

     

     

     

     

    Canvas의 Text를 GameManager 스크립트의 Score Text에 넣어준다.

     

     

     

     


     

    8. 코루틴(Coroutine)

     

     

    실행을 멈추었다가 다시 이어서 실행할 수 있는 기능이다.

    일정 시간 후 실행되거나, 특정 조건을 기다리는 등의 기능이다.

     

    GameObject를 생성하고, Coroutine의 스크립트를 추가한다.

     

     

     

     

    Coroutine 스크립트이다.

    using System.Collections;
    using UnityEngine;
    
    public class CoroutineStudy : MonoBehaviour
    {
        // 유니티 코루틴(Coroutine) 정리
        // 실행을 멈췄다가 다시 이어서 실행할 수 있는 기능
        // 일정 시간 후 실행되거나, 특정 조건을 기다린는 등의 기능을 쉽게 구현 가능
        void Start()
        {
            StartCoroutine("ExampleCoroutine"); //이름을 써도 되고
            StartCoroutine(ExampleCoroutine()); //함수를 써도 됨
        }
    
        IEnumerator ExampleCoroutine()
        {
            Debug.Log("코루틴 시작");
            yield return new WaitForSeconds(2f); //2초 대기
            Debug.Log("2초 후 실행");
        }
    }
    
    

     

     

     


     

     

    10. 시작 카운드다운 글자 생성

     

     

    GameManager 스크립트 수정

    아래 코드 추가

    public Text StartText; //게임시작전3,2,1
    
    IEnumerator StartGame()
    {
        int i = 3;
        while(i>0)
        {
            StartText.text = i.ToString();
    
            yield return new WaitForSeconds(1);
    
            i--;
    
            if(i == 0)
            {
                StartText.gameObject.SetActive(false); // UI 감추기
            }
        }
    }
    

     

     

     

    추가한 전체 코드

    using System.Collections;
    using UnityEngine;
    using UnityEngine.UI;
    
    public class GameManager : MonoBehaviour
    {
        //싱글톤 : 디자인 패턴
        public static GameManager intance;
        public Text scoreText; //점수를 표시하는 Text 객체를 에디터에서 받아옴
        public Text StartText; //게임시작전3,2,1
    
        int score = 0; //점수를 관리한다.
    
        private void Awake()
        {
            if(intance == null) //정적으로 자신을 체크한다. null인지
            {
                intance = this; //자기 자신을 저장한다.
            }
        }
    
        void Start()
        {
            StartCoroutine("StartGame");
        }
    
        IEnumerator StartGame()
        {
            int i = 3;
            while(i>0)
            {
                StartText.text = i.ToString();
    
                yield return new WaitForSeconds(1);
    
                i--;
    
                if(i == 0)
                {
                    StartText.gameObject.SetActive(false); // UI 감추기
                }
            }
        }
    
        public void AddScore(int num)
        {
            score += num; //점수를 더해준다.
            scoreText.text = "Score: " + score; //텍스트에 반영한다.
        }
    }
    
    

     

     

     

    원하는 텍스트를 만들고, 스크립트를 붙인 다음 실행하면 된다.

     

    이렇게 되면 얼추 완성 끝!!!!

     

     

     

     

     

     


     

    유명한 게임 드래곤 플라이트를 실습해 보았다.

    기존에 C#만 배울 때에서 응용되는 것도 많았고,

    유니티 안에서의 기능으로 편리하게 사용할 수 있다는 점도 신기하였다.

     

    어찌 보면 시각적으로 볼 수 있는 즐거움이 있기에

    유니티는 배우기 더 직관적으로 어렵지 않은 느낌이었다.

     

    하지만 점점 코드가 많아지고, 이 스크립트 저 스크립트를

    왔다 갔다 하니까 뭐가 어떤 게 있었는지 기억하기가 조금 어려웠다..

     

    이게 왜 이 스크립트에 들어 가는지에 대해서 다시 복습을 해볼 필요도 있다..

    그리고 혼자 공부할 때보다는 유용한 유니티 정보가 많아서 좋았다..

     

    항상 GPT에게 물어보면 코드로 적용하는데..

    유니티의 기능을 몰라도 너무 몰랐다..

     

     

    아무튼 재밌었다!


    목차