Version: 2020.2
언어: 한국어
변수와 인스펙터
이벤트 함수의 실행 순서

런타임 시점에 프리팹 인스턴스화

프리팹은 복잡한 게임 오브젝트나 게임 오브젝트 컬렉션을 런타임 시점에 인스턴스화하려는 경우에 매우 유용합니다. 코드를 사용하여 처음부터 게임 오브젝트를 만드는 것보다는 코드를 사용하여 프리팹을 인스턴스화하는 것이 다음과 같은 많은 이점이 있습니다.

  • 한 줄의 코드를 사용하여 프리팹을 인스턴스화합니다. 이에 상응하는 게임 오브젝트를 처음부터 만드는 데는 훨씬 많은 코드 줄이 필요합니다.

  • 씬 뷰, 계층 구조인스펙터를 사용하여 프리팹을 빠르고 쉽게 설정하고 테스트하고 수정합니다.

  • 코드를 변경하지 않고도 인스턴스화할 프리팹을 변경합니다. 즉, 코드 변경 없이도 단순한 로켓을 초강력 로켓으로 바꿀 수 있습니다.

참고: 이 페이지의 모든 예제가 포함된 Unity 프로젝트는 다음에서 다운로드할 수 있습니다.

InstantiatingPrefabsExamples.zip

프리팹 인스턴스화의 기본 사항

런타임 시점에 프리팹을 인스턴스화하려면 코드에 해당 프리팹에 대한 레퍼런스가 있어야 합니다. 프리팹 레퍼런스를 보관하기 위해 코드에 public 변수를 생성하면 이러한 레퍼런스를 만들 수 있습니다. 코드의 public 변수는 인스펙터에 할당 가능한 필드로 나타납니다. 그런 다음 인스펙터에서 사용할 실제 프리팹을 할당할 수 있습니다.

아래 스크립트 예시에는 단일 public 변수 “myPrefab”이 있습니다. 이 변수는 프리팹에 대한 레퍼런스이며 Start() 메서드에서 해당 프리팹의 인스턴스를 만듭니다.

using UnityEngine;
public class InstantiationExample : MonoBehaviour 
{
    // Reference to the Prefab. Drag a Prefab into this field in the Inspector.
    public GameObject myPrefab;

    // This script will simply instantiate the Prefab when the game starts.
    void Start()
    {
        // Instantiate at position (0, 0, 0) and zero rotation.
        Instantiate(myPrefab, new Vector3(0, 0, 0), Quaternion.identity);
    }
}

예제를 사용하는 방법

  • 프로젝트에 새로운 C# 스크립트를 만들고 “InstantiationExample”라는 이름을 지정합니다.

  • 위의 스크립트 예제를 복사하여 새로운 스크립트에 붙여 넣은 후 저장합니다.

  • GameObject > Create Empty 메뉴를 사용하여 빈 게임 오브젝트를 만듭니다.

  • 스크립트를 빈 게임 오브젝트로 드래그하여 스크립트를 컴포넌트로 새 게임 오브젝트에 추가합니다.

  • 프리팹을 생성한 후 프로젝트 창에서 스크립트 컴포넌트의 My Prefab 필드로 드래그합니다.

프리팹을 프로젝트 창에서 스크립트 컴포넌트의 My Prefab 필드로 드래그
프리팹을 프로젝트 창에서 스크립트 컴포넌트의 My Prefab 필드로 드래그

Play 모드를 시작하면 프리팹이 씬의 (0, 0, 0) 포지션에서 인스턴스화됩니다.

인스펙터에서 다른 프리팹을 My Prefab 필드로 드래그하면 스크립트를 변경하지 않고도 인스턴스화할 프리팹을 변경할 수 있습니다.

첫 번째 예시는 너무나 간단해서 프리팹을 씬에 직접 배치하는 것보다 별다른 이점이 없다고 생각할 수 있습니다. 하지만 코드를 사용한 프리팹 인스턴스는 게임이나 앱이 실행 중일 때도 게임 오브젝트의 복잡한 설정을 동적으로 생성할 수 있도록 강력한 기능을 제공합니다. 다음 예시를 참조하십시오.

일반적인 시나리오

런타임 시점의 프리팹 인스턴스화는 다음과 같은 일반적인 상황에서 매우 유용하게 사용될 수 있습니다.

  • 단일 프리팹을 다양한 포지션(예: 그리드 또는 원 배열)으로 여러 번 복제하여 구조체를 빌드합니다.

  • 런처에서 발사체 프리팹을 발사합니다. 발사체 프리팹은 메시, 리지드바디, 콜라이더, 오디오 소스, 동적 광원, 그리고 자체 트레일 파티클 시스템이 포함된 자식 게임 오브젝트가 들어 있는 복잡한 설정일 수 있습니다.

  • 차량, 건물 또는 캐릭터(예: 로봇)가 여러 조각으로 부서집니다. 이 시나리오에서 예제 스크립트는 완전히 동작하는 로봇 프리팹을 삭제한 후 부서진 로봇 프리팹으로 교체합니다. 이 부서진 프리팹은 로봇의 망가진 파트로 구성되며, 각 파트는 고유한 파티클 시스템과 리지드바디로 설정됩니다. 이 방식을 이용하면 한 줄의 코드로 로봇을 여러 조각으로 폭파할 수 있습니다. 즉, 원본 게임 오브젝트를 부서진 프리팹으로 교체합니다.

다음 섹션에서는 이러한 시나리오를 구현하는 방법을 보여드리겠습니다.

구조체 빌드

코드를 사용하여 특정한 설정의 프리팹 복사본을 대규모로 즉시 만들 수 있습니다. 코드를 사용하여 이런 식으로 구조체를 생성하는 것을 절차적 생성이라고 부릅니다. 아래 예시에서는 블록 인스턴스 벽을 만듭니다.

이 예제를 실습해 보려면 아래 스크립트를 작성하고 Wall이라는 이름을 지정한 후 씬의 빈 게임 오브젝트에 배치하십시오.

using UnityEngine;
public class Wall : MonoBehaviour
{
   public GameObject block;
   public int width = 10;
   public int height = 4;
  
   void Start()
   {
       for (int y=0; y<height; ++y)
       {
           for (int x=0; x<width; ++x)
           {
               Instantiate(block, new Vector3(x,y,0), Quaternion.identity);
           }
       }       
   }
}

작업을 마치면 인스펙터에 Block 변수가 표시되고 필드에 None이라는 단어가 보입니다. “None” 값은 이 변수에 아직 아무 프리팹도 할당되지 않았음을 의미합니다.

아직 아무 프리팹도 할당되지 않은 Block 변수
아직 아무 프리팹도 할당되지 않은 Block 변수

위 스크립트 예제는 프리팹을 Block 변수에 할당하기 전까지는 동작하지 않습니다. 간단한 블록 프리팹을 만들려면 다음 단계를 따르십시오.

  1. GameObject > 3D Object > Cube를 선택합니다.

  2. 큐브를 계층 구조 에서 프로젝트 Assets 폴더로 드래그합니다. 그러면 프리팹 에셋이 생성됩니다.

  3. 프리팹의 이름을 “Block”으로 변경합니다.

  4. 이제 Block 프리팹이 에셋으로 존재하므로 계층 구조에서 큐브를 안전하게 삭제할 수 있습니다.

Block 프리팹을 만들고 나면 Block 변수에 할당할 수 있습니다. 원본 게임 오브젝트(“Wall” 스크립트가 연결된 게임 오브젝트)를 선택한 후 “Block” 프리팹을 프로젝트 창에서 “Block” 변수 슬롯(“None”이라고 나와 있음)으로 드래그하십시오.

Block 프리팹이 할당된 Block 변수
Block 프리팹이 할당된 Block 변수

이 설정을 완료한 후 Play를 클릭하면 Unity가 해당 프리팹을 사용하여 벽을 빌드합니다.

위 예제에서 생성된 10개 블록 4열로 빌드된 벽
위 예제에서 생성된 10개 블록 4열로 빌드된 벽

이러한 유연한 워크플로 패턴은 Unity에서 반복해서 사용할 수 있습니다. 이 스크립트에서는 프리팹을 사용하기 때문에 이 프리팹을 간단하게 교체하거나 편집하여 스크립트를 수정하지 않고도 벽의 벽돌 프로퍼티를 수정할 수 있습니다. 또한 다른 프리팹이 할당된 씬의 다른 게임 오브젝트에 벽 스크립트를 사용하여 다른 프리팹 타입으로 다양한 벽을 만들 수도 있습니다.

코드를 사용하여 그리드, 원형 패턴, 무작위 분산, 또는 제작하는 게임이나 앱에 적합하다고 생각되는 여타 설정으로 게임 오브젝트를 배치할 수 있습니다. 다음은 원형 배열에 인스턴스를 배치하는 또 다른 예입니다.

using UnityEngine;
public class CircleFormation : MonoBehaviour
{
   // Instantiates prefabs in a circle formation
   public GameObject prefab;
   public int numberOfObjects = 20;
   public float radius = 5f;
   void Start()
   {
       for (int i = 0; i < numberOfObjects; i++)
       {
           float angle = i * Mathf.PI * 2 / numberOfObjects;
           float x = Mathf.Cos(angle) * radius;
           float z = Mathf.Sin(angle) * radius;
           Vector3 pos = transform.position + new Vector3(x, 0, z);
           float angleDegrees = -angle*Mathf.Rad2Deg;
           Quaternion rot = Quaternion.Euler(0, angleDegrees, 0);
           Instantiate(prefab, pos, rot);
       }
   }
}
위 예제에서 생성된 원형 블록 배열
위 예제에서 생성된 원형 블록 배열

발사체 및 폭발 인스턴스화

이 시나리오에서는 다음을 수행하십시오.

  1. 플레이어가 발사 버튼을 누르면 “런처” 게임 오브젝트가 발사체 프리팹을 인스턴스화합니다. 이 프리팹에는 메시, 리지드바디, 콜라이더가 포함되어 있어서 공중으로 날아가다가 충돌이 발생하면 감지할 수 있습니다.

  2. 이 발사체가 무언가와 충돌하면 폭발 프리팹이 인스턴스화됩니다. 폭발 프리팹에는 파티클 시스템 효과, 그리고 주변 게임 오브젝트에 힘을 가하는 스크립트가 포함됩니다.

위의 블록 프리팹과 같은 방식으로 발사체 프리팹의 복잡도와 관계없이 단 한 줄의 코드로 발사체를 인스턴스화할 수 있습니다. 프리팹을 인스턴스화한 후에는 인스턴스화한 게임 오브젝트의 프로퍼티를 수정할 수도 있습니다. 예를 들어 발사체 리지드바디의 속도를 설정할 수 있습니다.

사용이 간편할 뿐만 아니라, 코드를 만지지 않고도 나중에 프리팹을 수정할 수 있습니다. 따라서 발사체가 로켓이면 나중에 파티클 시스템을 추가하여 로켓이 구름 꼬리를 뒤에 남기도록 만들 수 있습니다. 이렇게 하면 모든 인스턴스화된 로켓에 파티클 트레일이 포함됩니다.

다음은 Instantiate() 함수를 사용하여 발사체를 발사하는 스크립트의 예입니다.

using UnityEngine;
public class FireProjectile : MonoBehaviour
{
    public Rigidbody projectile;
    public float speed = 4;
    void Update()
    {
        if (Input.GetButtonDown("Fire1"))
        {
            Rigidbody p = Instantiate(projectile, transform.position, transform.rotation);
            p.velocity = transform.forward * speed;
        }
    }
}

이 코드에서 프리팹 변수 타입은 게임 오브젝트가 아니라 리지드바디입니다. 이 경우 다음과 같은 두 가지 장점이 있습니다.

  1. Rigidbody 컴포넌트가 있는 게임 오브젝트만 이 변수에 할당할 수 있습니다. 이를 통해 올바른 게임 오브젝트를 변수에 할당하는지 쉽게 확인할 수 있습니다.

  2. Instantiate 메서드가 새 인스턴스에서 Rigidbody 컴포넌트에 대한 레퍼런스를 반환합니다. 따라서 인스턴스화한 후에 리지드바디의 속도를 간편하게 설정할 수 있습니다.

public 프리팹 변수를 만들 때 변수 타입은 게임 오브젝트, 또는 아무 유효한 컴포넌트 타입(빌트인 Unity 컴포넌트 또는 자체 MonoBehaviour 스크립트 중 하나)일 수 있습니다.

게임 오브젝트 타입 변수의 경우 변수에 게임 오브젝트를 할당하면 Instantiate 함수가 새로운 게임 오브젝트 인스턴스에 대한 레퍼런스를 반환합니다.

컴포넌트 타입 변수(예: 리지드바디, 콜라이더, 광원)의 경우 해당 컴포넌트 타입의 게임 오브젝트만 변수에 할당할 수 있습니다. 그러면 Instantiate 함수가 새로운 게임 오브젝트 인스턴스에서 해당 컴포넌트에 대한 레퍼런스를 반환합니다.

다음 스크립트(발사체 프리팹에 배치됨)는 발사체의 현재 포지션에서 폭발을 인스턴스화하고, 발사체가 무언가와 충돌하면 발사체 게임 오브젝트를 제거합니다.

using UnityEngine;
public class Projectile : MonoBehaviour
{
   public GameObject explosion;
   void OnCollisionEnter()
   {
       Instantiate(explosion,transform.position,transform.rotation);
       Destroy(gameObject);
   }
}
인스턴스화된 후 충돌 시 폭발 프리팹으로 교체되는 발사체 프리팹의 예
인스턴스화된 후 충돌 시 폭발 프리팹으로 교체되는 발사체 프리팹의 예

Play 모드에서 실행되는 스크립트를 보여주는 위 이미지에서 인스턴스화된 게임 오브젝트는 계층 구조에서 나타나며, 이름에 “(Clone)” 단어가 붙습니다.

캐릭터를 래그돌 또는 잔해로 대체

종종 게임에서 캐릭터, 차량, 건물 또는 기타 에셋을 “온전한” 상태에서 “파괴된” 상태로 전환해야 할 때가 있습니다. 이 경우 게임 오브젝트의 온전한 버전을 수정(예: 스크립트 제거, Rigidbody 컴포넌트 추가 등)하는 것보다는 온전한 게임 오브젝트를 삭제한 후 인스턴스화된 파괴된 프리팹으로 교체하는 것이 훨씬 더 효율적이고 효과적입니다. 이렇게 하면 더욱 높은 유연성을 확보할 수 있습니다. 파괴된 버전에 대해 다른 머티리얼을 사용하고, 완전히 다른 스크립트를 연결하거나, 여러 조각으로 부서진 게임 오브젝트가 포함된 프리팹을 인스턴스화하여 망가지고 부서진 원본 게임 오브젝트 버전을 시뮬레이션할 수 있습니다. 이러한 옵션들은 Instantiate()에 대한 단일 호출을 통해 구현할 수 있습니다. 즉, 파괴된 버전을 씬으로 가져오고 원본은 삭제합니다.

무엇보다도, 원본과 완전히 다른 게임 오브젝트를 사용하여 Instantiate()하는 파괴된 버전을 만들 수 있다는 점이 중요합니다. 예를 들어 파괴 가능한 로봇을 만들 때 두 가지 버전, 즉 메시 렌더러와 로봇 움직임에 대한 스크립트가 포함된 단일 게임 오브젝트로 구성된 버전과

물리를 통해 개별적으로 제어할 수 있는 여러 개의 골격 파트로 구성된 버전을 모델링할 수 있습니다. 하나의 게임 오브젝트만 포함된 모델을 사용하면 게임이 더 빠르게 실행됩니다. 이는 이 모델에 삼각형이 별로 없기 때문입니다. 따라서 작은 파트가 많은 로봇보다 렌더링 속도가 더 빠릅니다. 또한 로봇이 온전하게 돌아다니는 동안에는 별도 파트로 유지할 필요가 없습니다.

망가진 로봇 프리팹을 빌드하려면 다음 단계를 따르십시오.

  1. 원하는 3D 모델링 소프트웨어에서 많은 다양한 골격 파트로 로봇을 모델링한 후 Unity 프로젝트의 Assets 폴더로 익스포트합니다.

  2. Unity 에디터에 빈 씬을 만듭니다.

  3. 모델을 프로젝트 창에서 빈 씬으로 드래그합니다.

  4. 모든 파트를 선택하고 Component > Physics > Rigidbody를 선택하여 모든 파트에 리지드바디를 추가합니다.

  5. 모든 파트를 선택하고 Component > Physics > Mesh Collider를 선택(성능 향상을 위해 Convex 옵션 활성화)하여 모든 파트에 콜라이더를 추가합니다.

  6. 망가진 로봇의 모든 파트의 부모가 하나의 루트 게임 오브젝트인지 확인합니다.

  7. 연기 같은 파티클 시스템을 각 파트에 자식 게임 오브젝트로 추가하여 특수 효과를 더합니다.

  8. 이제 폭발 가능한 여러 개의 파트로 구성된 로봇이 완성되었습니다. 모든 파트들은 물리에 의해 제어되므로 바닥에 떨어질 수 있으며, 각 파트는 연결된 파티클 시스템으로 인해 파티클 트레일을 생성합니다.

  9. Play를 클릭하여 모델이 반응하는 방식을 미리본 후 필요한 사항을 조정합니다.

  10. 루트 게임 오브젝트를 프로젝트 창Assets 폴더로 드래그하여 새로운 프리팹을 만듭니다.

다음 예제는 이러한 단계를 코드로 모델링하는 방법을 보여줍니다.

using UnityEngine;
public class WreckOnCollision : MonoBehaviour
{
   public GameObject wreckedVersion;
   // Update is called once per frame
   void OnCollisionEnter()
   {
       Destroy(gameObject);
       Instantiate(wreckedVersion,transform.position,transform.rotation);
   }
}
발사체에 맞은 후 로봇 프리팹이 망가진 프리팹으로 교체되는 예
발사체에 맞은 후 로봇 프리팹이 망가진 프리팹으로 교체되는 예

모든 예제가 포함된 프로젝트는 다음에서 다운로드할 수 있습니다. InstantiatingPrefabsExamples.zip

변수와 인스펙터
이벤트 함수의 실행 순서