오브젝트 생성과 삭제

Unity에서 GameObject를 생성하고 삭제하는 방법을 학습합니다. Instantiate()와 Destroy() 함수를 다룹니다.

오브젝트 생성과 삭제란?

게임 개발에서 오브젝트를 동적으로 생성하고 삭제하는 것은 매우 중요한 기능입니다. 예를 들어, 총알을 발사할 때 총알 오브젝트를 생성하고, 화면 밖으로 나가면 삭제하는 등의 상황에서 사용합니다.

기본 개념

  • Instantiate(): 프리팹이나 오브젝트를 복제하여 새로 생성
  • Destroy(): 오브젝트를 메모리(memory)에서 제거
  • 프리팹: 재사용 가능한 오브젝트 템플릿

1. Instantiate() - 오브젝트 생성

Instantiate()는 프리팹이나 기존 오브젝트를 복제하여 새로운 오브젝트를 생성합니다.

기본 사용법

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    // 화살 프리팹 (Inspector에서 할당)
    public GameObject arrowPrefab;
    
    void Update()
    {
        // 프리팹을 복제하여 새 화살 생성
        GameObject newArrow = Instantiate(arrowPrefab);
    }
}

위치 지정하여 생성

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    public GameObject arrowPrefab;
    
    void Update()
    {
        // 특정 위치에 화살 생성
        Vector3 spawnPosition = new Vector3(0, 6f, 0);
        GameObject newArrow = Instantiate(arrowPrefab, spawnPosition, Quaternion.identity);
    }
}

랜덤 위치에 생성

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    public GameObject arrowPrefab;
    float spawnTime = 0.3f;
    float delta = 0f;

    void Update()
    {
        // 시간 누적
        delta += Time.deltaTime;
        
        // 일정 시간이 지나면 화살 생성
        if (delta > spawnTime)
        {
            delta = 0f;
            
            // 랜덤한 X 위치 계산
            float randomX = Random.Range(-8.4f, 8.4f);
            
            // 랜덤 위치에 화살 생성
            Vector3 spawnPosition = new Vector3(randomX, 6f, 0);
            GameObject newArrow = Instantiate(arrowPrefab, spawnPosition, Quaternion.identity);
        }
    }
}

Instantiate() 매개변수(variable)(parameter)

// 1. 프리팹만 지정 (현재 위치에 생성)
GameObject obj = Instantiate(prefab);

// 2. 위치 지정
GameObject obj = Instantiate(prefab, position, rotation);

// 3. 부모 지정
GameObject obj = Instantiate(prefab, parentTransform);

// 4. 위치, 회전, 부모 모두 지정
GameObject obj = Instantiate(prefab, position, rotation, parentTransform);

2. Destroy() - 오브젝트 삭제

Destroy()는 오브젝트를 메모리(memory)에서 제거합니다.

기본 사용법

using UnityEngine;

public class ArrowController : MonoBehaviour
{
    public float arrowSpeed = 0.1f;

    void Update()
    {
        // 화살을 아래로 이동
        transform.Translate(0, -arrowSpeed, 0);

        // 화면 밖으로 나갔는지 확인
        if (transform.position.y < -6f)
        {
            // 화살 삭제
            Destroy(gameObject);
        }
    }
}

다른 오브젝트 삭제

using UnityEngine;

public class EnemyController : MonoBehaviour
{
    void OnTriggerEnter2D(Collider2D other)
    {
        // 충돌한 오브젝트가 "Bullet" 태그를 가진 경우
        if (other.CompareTag("Bullet"))
        {
            // 총알 삭제
            Destroy(other.gameObject);
            
            // 자신도 삭제
            Destroy(gameObject);
        }
    }
}

지연 삭제

using UnityEngine;

public class Explosion : MonoBehaviour
{
    void Start()
    {
        // 2초 후에 삭제
        Destroy(gameObject, 2f);
    }
}

Destroy() 매개변수(variable)(parameter)

// 1. 즉시 삭제
Destroy(gameObject);

// 2. 지연 삭제 (초 단위)
Destroy(gameObject, 2f);

// 3. 컴포넌트만 삭제 (오브젝트는 유지)
Destroy(GetComponent<Rigidbody2D>());

3. 실제 사용 예제

예제 1: 화살 생성기

using UnityEngine;

public class ArrowGenerator : MonoBehaviour
{
    public GameObject arrowPrefab;
    float spawnTime = 0.3f;
    float delta = 0f;

    void Update()
    {
        // 시간 누적
        delta += Time.deltaTime;
        
        // 일정 시간이 지나면 화살 생성
        if (delta > spawnTime)
        {
            // 타이머 초기화
            delta = 0f;
            
            // 화살 프리팹을 복제하여 생성
            GameObject arrow = Instantiate(arrowPrefab);
            
            // 랜덤한 X 위치 설정
            float randomX = Random.Range(-8.4f, 8.4f);
            arrow.transform.position = new Vector3(randomX, 6f, 0);
        }
    }
}

예제 2: 화살 자동 삭제

using UnityEngine;

public class ArrowController : MonoBehaviour
{
    public float arrowSpeed = 0.1f;

    void Update()
    {
        // 화살을 아래로 이동
        transform.Translate(0, -arrowSpeed, 0);

        // 화면 아래로 나갔는지 확인
        if (transform.position.y < -6f)
        {
            // 화살 삭제
            Destroy(gameObject);
        }
    }
}

예제 3: 불덩어리 생성기

using UnityEngine;

public class FireballSpawner : MonoBehaviour
{
    public GameObject fireballPrefab;
    public float spawnHeight = 8f;
    public float minX = -5f;
    public float maxX = 5f;

    void Start()
    {
        // 불덩어리 생성 시작
        StartCoroutine(SpawnFireballs());
    }

    System.Collections.IEnumerator SpawnFireballs()
    {
        for (int i = 0; i < 10; i++)
        {
            // 랜덤 위치 계산
            float randomX = Random.Range(minX, maxX);
            Vector3 spawnPosition = new Vector3(randomX, spawnHeight, 0);
            
            // 불덩어리 생성
            GameObject fireball = Instantiate(fireballPrefab, spawnPosition, Quaternion.identity);
            fireball.SetActive(true);
            
            // 0.5초 ~ 2초 사이의 랜덤한 시간 대기
            float delay = Random.Range(0.5f, 2f);
            yield return new WaitForSeconds(delay);
        }
    }
}

4. 주의사항

1. null 체크

void Update()
{
    // ✅ 안전한 방법
    if (arrowPrefab != null)
    {
        GameObject arrow = Instantiate(arrowPrefab);
    }
}

2. 메모리(memory) 관리

// ❌ 나쁜 예: 무한정 생성
void Update()
{
    GameObject obj = Instantiate(prefab);
    // 삭제하지 않으면 메모리 누수 발생
}

// ✅ 좋은 예: 생성 후 적절히 삭제
void Update()
{
    GameObject obj = Instantiate(prefab);
    Destroy(obj, 5f); // 5초 후 자동 삭제
}

3. Destroy() 타이밍

void Update()
{
    // Destroy()는 즉시 실행되지 않음
    Destroy(gameObject);
    
    // ❌ 위험: 삭제된 오브젝트에 접근
    Debug.Log(gameObject.name); // 에러 발생 가능
    
    // ✅ 안전: return으로 즉시 종료
    Destroy(gameObject);
    return;
}

4. 프리팹 vs 인스턴스(instance)

public GameObject arrowPrefab; // 프리팹 (원본)

void Start()
{
    // 프리팹을 복제하여 인스턴스 생성
    GameObject arrow1 = Instantiate(arrowPrefab);
    GameObject arrow2 = Instantiate(arrowPrefab);
    
    // arrow1과 arrow2는 서로 다른 오브젝트
    // arrowPrefab은 원본이므로 변경하지 않음
}

5. 고급 사용법

오브젝트 풀링 (Object Pooling)

많은 오브젝트를 생성/삭제할 때는 오브젝트 풀링을 사용하는 것이 효율적입니다.

using UnityEngine;
using System.Collections.Generic;

public class ObjectPool : MonoBehaviour
{
    public GameObject prefab;
    public int poolSize = 10;
    private List<GameObject> pool = new List<GameObject>();

    void Start()
    {
        // 미리 오브젝트 생성
        for (int i = 0; i < poolSize; i++)
        {
            GameObject obj = Instantiate(prefab);
            obj.SetActive(false);
            pool.Add(obj);
        }
    }

    // 사용 가능한 오브젝트 가져오기
    public GameObject GetObject()
    {
        foreach (GameObject obj in pool)
        {
            if (!obj.activeInHierarchy)
            {
                obj.SetActive(true);
                return obj;
            }
        }
        
        // 모두 사용 중이면 새로 생성
        GameObject newObj = Instantiate(prefab);
        pool.Add(newObj);
        return newObj;
    }

    // 오브젝트 반환 (비활성화)
    public void ReturnObject(GameObject obj)
    {
        obj.SetActive(false);
    }
}

6. 정리

Instantiate()

  • 용도: 프리팹이나 오브젝트를 복제하여 생성
  • 위치 지정: Instantiate(prefab, position, rotation)
  • 주의: null 체크 필수, 메모리(memory) 관리 중요

Destroy()

  • 용도: 오브젝트를 메모리(memory)에서 제거
  • 즉시 삭제: Destroy(gameObject)
  • 지연 삭제: Destroy(gameObject, delay)
  • 주의: 삭제 후 접근하지 않기

베스트 프랙티스

  1. 필요할 때만 생성: 불필요한 생성은 성능 저하
  2. 적절히 삭제: 메모리(memory) 누수 방지
  3. 프리팹 사용: 재사용 가능한 오브젝트는 프리팹으로
  4. 오브젝트 풀링: 자주 생성/삭제하는 경우 고려

연습 문제

  1. 스페이스바를 누르면 총알을 생성하는 스크립트를 작성하세요.

  2. 화면 밖으로 나간 오브젝트를 자동으로 삭제하는 스크립트를 작성하세요.

  3. 1초마다 랜덤한 위치에 코인을 생성하고, 5초 후에 자동으로 삭제하는 스크립트를 작성하세요.