티스토리 뷰

오늘은 게임 하나를 완성했다 수업에서 완전히 만들지 않고 과제로 더 발전시켜서 완성시키는 거였다 어제는 유니티를 만지는게 어려웠는데 나름 하루만에 익숙해진 것 같다 ㅎ


💻 Unity

Collider

게임 오브젝트의 물리적 경계를 정의하는 컴포넌트. 충돌 감지와 물리 상호작용의 핵심 요소로 RigidBody와 함께 사용하면 물리 엔진의 영향을 받는다.

Collider 설정과 속성 ⚙️

  • Is Trigger: 체크하면 물리적 충돌 없이 충돌 감지만 수행 (한쪽만 Trigger 충돌이 돼있어도 됨)
  • Material: 물리적 속성(마찰, 탄성)을 정의하는 Physics Material
  • Size/Radius: 콜라이더의 크기 조절
  • Center: 콜라이더의 **OnCollisionEnter2D() vs OnTriggerEnter2D()**중심점 조절

충돌 규칙

  • 충돌 감지를 위해선 적어도 하나의 Rigidbody가 필요함.
  • 둘 다 isTrigger=false면, OnCollisionEnter2D()를 사용해야 함.
  • OnTriggerEnter2D()를 사용하려면, 적어도 하나의 Collider2D가 isTrigger=true여야 함.

활용 사례 💡

  • 물리 충돌: 캐릭터가 벽과 충돌해 멈추는 등의 물리 반응
  • 아이템 획득: 트리거를 사용해 플레이어가 아이템을 획득
  • 영역 감지: 특정 구역 진입/이탈 감지 (세이프존, 데미지존 등)
  • 레이캐스팅: 콜라이더 대상으로 광선 투사로 물체 감지
  • 센서: 적 감지, 시야 범위 구현 등

충돌 감지 이벤트

  • 일반 충돌 이벤트 (IsTrigger 활성화)
// 충돌이 시작될 때
void OnCollisionEnter(Collision collision)
{
    Debug.Log("충돌 시작: " + collision.gameObject.name);
}

// 충돌 중일 때 (매 프레임)
void OnCollisionStay(Collision collision)
{
    Debug.Log("충돌 중");
}

// 충돌이 끝날 때
void OnCollisionExit(Collision collision)
{
    Debug.Log("충돌 종료");
}
  • 트리거 이벤트 (IsTrigger 비활성화)
// 트리거 영역 진입 시
void OnTriggerEnter(Collider other)
{
    Debug.Log("트리거 진입: " + other.gameObject.name);
}

// 트리거 영역 내에 있을 때 (매 프레임)
void OnTriggerStay(Collider other)
{
    Debug.Log("트리거 영역 내 체류 중");
}

// 트리거 영역 이탈 시
void OnTriggerExit(Collider other)
{
    Debug.Log("트리거 이탈");
}

OnCollisionEnter2D() vs OnTriggerEnter2D()

특성  OnCollisionEnter2D  OnTriggerEnter2D
콜라이더 설정 IsTrigger = false IsTrigger = true
물리적 충돌 ✅ 있음 (반발, 밀림) ❌ 없음 (통과)
물리력 정보 Collision2D 객체 제공 제공하지 않음
성능 영향 물리 계산 필요 단순 감지만 수행

Prefab

재사용 가능한 게임 오브젝트로 한 번 만들고 여러 번 사용할 수 있다. 프로젝트 창에서 파란색 큐브 아이콘으로 표시된다.

Prefab 사용 방법

  1. 게임 오브젝트를 씬에 만들고 필요한 컴포넌트 추가
  2. 하이어라키에서 프로젝트 창으로 드래그

활용 사례 💡

  • 적 캐릭터: 동일한 적을 여러 위치에 배치
  • 총알/발사체: 반복적으로 생성되는 게임 요소
  • 환경 요소: 나무, 바위, 건물 등 레벨 디자인 요소
  • UI 요소: 버튼, 패널 등 동일한 스타일의 UI
  • 파티클 효과: 폭발, 연기 등의 특수 효과

Prefab 인스턴스 생성

// 인스펙터에서 할당할 변수
public GameObject enemyPrefab;

// 런타임에 프리팹 생성
void SpawnEnemy(Vector3 position)
{
    Instantiate(enemyPrefab, position, Quaternion.identity);
}

Coroutine

시간이 걸리는 작업을 처리하기 위한 함수. 일반 함수와 달리 실행을 일시중지 했다가 나주에 재개할 수 있다

void Start() 
{
    StartCoroutine(MyCoroutine());
}

IEnumerator MyCoroutine() 
{
    Debug.Log("시작!");
    yield return new WaitForSeconds(2f);  // 2초 대기
    Debug.Log("완료!");
}

// 코루틴 중지하기
void StopMyCoroutine() 
{
    StopCoroutine(MyCoroutine());
    // StopAllCoroutines(); // 모든 코루틴 중지
}

활용 사례 💡

  • 캐릭터 애니메이션 시퀀스
  • 시간 기반 이벤트와 타이머
  • UI 페이드 인/아웃 효과
  • 점진적인 값 변경 (부드러운 움직임)
  • 게임 데이터 로딩
특징  코루틴 일반 함수
반환 타입 IEnumerator void, int 등
실행 방식 중간에 일시정지 가능 한 번에 모두 실행
호출 방법 StartCoroutine() 직접 호출
시간 지연 yield 문으로 가능 불가능 (UI 차단)

🕹️ 게임 실습

DragonFlight

Player

  • Player 태그
  • Circle Colider 2D 추가 후 충돌 범위 설정
  • Rigidbody 2D 중력 줄 필요 X > Gravity Scale 0
public float moveSpeed = 5.0f;

void FixedUpdate()
{
    moveControl();
}

void moveControl()
{
    float distanceX = Input.GetAxis("Horizontal") * moveSpeed * Time.deltaTime;
    transform.Translate(distanceX, 0, 0);
}

Wall

  • Box Colider 2D 추가

Enemy

  • Circle Colider 2D 추가 후 IsTrigger 체크 > player와 부딪혀도 그냥 지나가게 함 (충돌 감지만 트리거만 수행하고 물리적 충돌을 일으키지 않음)
  • Project에 드래그해 Prefab으로 만듦
  • Enemy 태그
public float moveSpeed = 2.0f;

void Update()
{
    float distanceY = moveSpeed * Time.deltaTime;
    transform.Translate(0, -distanceY, 0);
}

private void OnBecameInvisible() // 카메라에서 보이지 않으면 호출
{
    Destroy(gameObject); // 오브젝트 삭제
}

Bullet

  • Order in layer 1로 설정
  • Box Collider 2D 추가 후 IsTrigger 체크
  • Rigidbody 추가 후 Gravity Scale 0
public float moveSpeed = 1.8f;
public GameObject explosion; // Explosion B 연결

void Update()
{
    transform.Translate(0, moveSpeed * Time.deltaTime, 0);
}

private void OnBecameInvisible()
{
    Destroy(gameObject);
}

private void OnTriggerEnter2D(Collider2D collision) // 충돌 트리거
{
    if (collision.gameObject.CompareTag("Enemy"))
    {
        // 폭발 이펙트 생성
        Instantiate(explosion, transform.position, Quaternion.identity);
        SoundManager.Instance.PlayDieSound();

        // 적과 자신 지우기
        Destroy(collision.gameObject);
        Destroy(gameObject);
        
        GameManager.Instance.AddScore(10);
    }
}

Launcher

  • Player 하위에 Empty Object 생성
public GameObject bullet; // bullet 연결

void Start()
{
    // InvokeRepeating(함수 이름, 초기 지연시간, 지연할 시간)
    InvokeRepeating("Shoot", 0.5f, 0.5f);
}

void Shoot()
{
    // 미사일 프리팹, 런처 포지션, 방향값 안줌
    Instantiate(bullet, transform.position, Quaternion.identity);
    SoundManager.Instance.PlayBulletSound();
}

SpawnManager

  • Empty Object 생성
public GameObject enemy; // samll_enemy 연결

void Start()
{
    InvokeRepeating("SpawnEnemy", 1.0f, 0.5f);
}

void SpawnEnemy()
{
    float randomX = Random.Range(-2f, 2f);
    Instantiate(enemy, new Vector3(randomX, transform.position.y, 0f), Quaternion.identity);
}

SoundManager

  • Audio Source 추가
public static SoundManager Instance { get; set; }
AudioSource myAudio; // AudioSource 컴포넌트
public AudioClip BulletSound; // se_attack 연결
public AudioClip DieSound; // baby_dragon_die 연결

private void Awake()
{
    if (SoundManager.Instance == null)
    {
        SoundManager.Instance = this;
    }
}

void Start()
{
    myAudio = GetComponent<AudioSource>();
}

public void PlayBulletSound()
{
    myAudio.PlayOneShot(BulletSound);
}

public void PlayDieSound()
{
    myAudio.PlayOneShot(DieSound);
}

UI / GameManager

  • UI > Legacy > Text 오브젝트 추가
  • GameManager 오브젝트 추가
public static GameManager Instance { get; set; }
public Text scoreText;
public int Score { get; set; }

private void Awake()
{
    if (GameManager.Instance == null)
    {
        GameManager.Instance = this;
    }
}

public void AddScore(int num)
{
    Score += num;
    scoreText.text = $"Score : {Score}";
}

💫 Tip!

Anchor Presets

Alt 누르면 붙일 수 있도록 변경됨


📝 과제

DragonFligh 완성

https://github.com/yh97yhyh/likelion-unity-study/tree/main/DragonFlight

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
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
글 보관함