Saving Your Work
입력

런타임에서의 프리팹의 인스턴스화

여기서는 Prefabs의 중요한 개념을 이해할 수 있을 것입니다. 이들은 게임을 통해 다시 이용할 수 있는 미리 정의된 GameObjectsComponents입니다. 만약 프리팹이 무엇인지 모르는 경우, Prefabs(Prefabs)에 보다 기본적인 설명이 있으니 참조해주세요.

프리팹은 복잡한 게임 오브젝트를 런타임에 인스턴스화하려는 경우에 유용합니다. 프리팹 인스턴스화에 대한 대안은 처음부터 코드를 사용하여 게임 오브젝트를 만드는 것입니다. 프리팹 인스턴스화는 이 대안에 비해 많은 장점이 있습니다 :

  • 프리팹 인스턴스화 코드 한 줄로 완전한 기능성을 가질 수 있습니다. 같은 기능을 하는 게임오브젝트를 코드로 작성하려면, 평균 다섯 줄, 실제로는 그 이상 소요됩니다.
  • 씬 및 인스펙터에서 빠르고 쉽게 프리팹을 설치, 테스트 및 수정할 수 있습니다.
  • 인스턴스화에 사용한 코드를 변경하지 않고 인스턴스화 된 프리팹을 변경할 수 있습니다. 간단한 로켓을 빠른 로켓으로 변경할 수 있어 코드의 변경이 필요 없습니다.

일반적인 시나리오

프리팹의 강점을 확인하기 위해 간단한 몇가지 상황을 생각해 봅시다 :

  1. 하나의 “벽돌” Prefab으로 벽을 만들기 위해 여러 번 다른 위치에 생성합니다.
  2. 로켓 런처는 사용자가 발사 버튼을 누르면 로켓 프리팹을 인스턴스화합니다. 프리팹은 메쉬, Rigidbody, 콜라이더 및 트레일 파티클 시스템을 포함하는 자식 게임 오브젝트로 구성됩니다.
  3. 로봇이 여러 조각으로 폭발합니다. 완전한 기능의 로봇은 파괴되고, 손상된 로봇 프리팹으로 바뀝니다. 이 프리팹은 여러 부분으로 분할된 로봇을 포함, 모두 각각의 Rigidbody와 파티클 시스템에 설치되어 있습니다. 이 기술을 사용하면 로봇을 여러 부분으로 폭발시키는 데 한 줄의 코드만으로 오브젝트를 프리팹으로 대체하여 실현할 수 있습니다.

벽 만들기

이 샘플에서는 프리팹 사용을 코드에서 오브젝트 생성하여 사용하는 것과 비교하여 장점을 확인하고 있습니다.

먼저, 코드에서 벽돌 벽을 만듭니다:

// JavaScript
function Start () {
    for (var y = 0; y < 5; y++) {
        for (var x = 0; x < 5; x++) {
            var cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
            cube.AddComponent.<Rigidbody>();
            cube.transform.position = Vector3 (x, y, 0);
        }
    }
}


// C#
public class Instantiation : MonoBehaviour {

    void Start() {
        for (int y = 0; y < 5; y++) {
            for (int x = 0; x < 5; x++) {
                GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
                cube.AddComponent<Rigidbody>();
                cube.transform.position = new Vector3(x, y, 0);
            }
        }
    }
}
  • 위의 스크립트를 사용하려면, 간단하게 스크립트를 저장하여 빈 게임 오브젝트에 드래그 앤 드롭합니다.
  • 빈 게임 오브젝트를 GameObject->Create Empty로 만듭니다.

만약 코드로 실행하면 Play 모드에 들어갔을 때 벽돌 벽 전체를 볼 수 있습니다. 별도의 벽돌 기능과 관련된 부분이 두 줄 있습니다 : CreatePrimitive행 및 AddComponent행입니다. 여기까지 나쁘지는 않습니다만, 각각의 벽돌은 텍스처가 없습니다. 하고자 하는 모든 추가 작업은 한 줄마다 필요합니다. 예를 들어 텍스처 변경, 마찰, Rigidbody의 mass(중량)는 다른 행에 있습니다.

If you create a Prefab and perform all your setup before-hand, you use one line of code to perform the creation and setup of each brick. This relieves you from maintaining and changing a lot of code when you decide you want to make changes. With a Prefab, you just make your changes and Play. No code alterations required.

만약 각 벽돌 프리팹을 사용하는 경우, 다음 코드에서 벽을 만들 수 있습니다.

// JavaScript

var brick : Transform;
function Start () {
    for (var y = 0; y < 5; y++) {
        for (var x = 0; x < 5; x++) {
            Instantiate(brick, Vector3 (x, y, 0), Quaternion.identity);
        }
    }
}


// C#
public Transform brick;

void Start() {
    for (int y = 0; y < 5; y++) {
        for (int x = 0; x < 5; x++) {
            Instantiate(brick, new Vector3(x, y, 0), Quaternion.identity);
        }
    }
}

이것은 깔끔할 뿐만 아니라 재사용률도 높은 것입니다. 큐브를 인스턴스화하거나 rigidbody를 포함할 필요가 있다고 써있는 곳은 없습니다. 이 모두는 프리팹으로 정의되어 에디터에서 신속하게 만들 수 있습니다.

이제 프리팹을 만들고 이것을 에디터에서 실행합니다. 다음은 그것을 수행하는 방법입니다 :

  1. GameObject > 3D Object > Cube 선택
  2. Component > Physics > Rigidbody를 선택
  3. Assets > Create > Prefab을 선택
  4. Project View에서 프리팹 이름을 “Brick”으로 변경합니다.
  5. Hierarchy에서 만든 큐브를 Project View의 “Brick” 프리팹에 드래그 앤 드롭합니다.
  6. 만들어진 큐브 프리팹을 프리팹을 (Windows에서는 Delete, Mac에서는 Command-Backspace)로 계층뷰에서 안전하게 제거할 수 있습니다.

벽돌 프리팹을 만들었으므로, 이제 스크립트 brick 변수에 연결해야 합니다. 스크립트가 포함된 빈 게임 오브젝트를 선택합니다. 인스펙터에서 “brick”이라는 새 변수가 표시되는 것을 확인하십시오.

다음으로, 프로젝트 뷰에서 “Brick” 프리팹을 인스펙터의 brick 변수에 드래그 앤 드롭합니다. Play를 클릭하면 프리팹으로 만들어진 벽이 표시됩니다.

이 워크 플로우 패턴은 Unity에서 여러 번 반복 사용할 수 있는 것입니다. 처음에는, 스크립트에서 만들면 두 줄도 안 되는 것을, 왜 이렇게 하는 것이 좋은 지 모를지도 모릅니다.

하지만 지금은 프리팹을 사용하고 있기 때문에, 프리팹 수정을 몇 초만으로 할 수 있습니다. 모든 인스턴스의 무게를 변경하고 싶으면? 그런 경우 프리팹 Rigidbody를 한 번 조정하면 됩니다. 모든 인스턴스 Material을 다른 것으로 변경하고 싶으면? 메테리얼을 프리팹에 한 번만 드래그 앤 드롭하면 됩니다. 마찰을 변경하려면? 프리팹 콜라이더에서 Physic Material를 변경하면 됩니다. 모든 상자에 파티클 시스템을 추가하고 싶다면? 프리팹으로 자식 오브젝트를 한 번 추가하기만 하면 됩니다.

로켓 및 폭발의 인스턴스화

알맞은 프리팹 사용법은 다음과 같습니다 :

  1. 로켓 런처는 사용자가 발사 버튼을 누르면 로켓 프리팹을 인스턴스화합니다. 프리팹은 메쉬, Rigidbody, 콜라이더 및 트레일 파티클 시스템을 포함하는 자식 게임 오브젝트로 구성됩니다.
  2. 로켓은 발사되어, 폭발 프리팹을 인스턴스화합니다. 폭발 프리팹은 파티클 시스템, 시간을 지난후 페이드 아웃하는 라이트 및 주위의 게임 오브젝트에 데미지를 입히는 스크립트를 포함합니다.

로켓 게임 오브젝트를 완전히 코드에서 수동으로 컴포넌트를 추가하고 프로퍼티를 설정하여 만들 수도 있지만, 프리팹을 인스턴스화하는 것이 훨씬 쉽습니다. 로켓의 인스턴스화를 로켓의 프리팹 복잡성에 관계없이 한 줄의 코드에서 할 수 있습니다. 프리팹을 인스턴스화 한 후 추가 인스턴스화 된 오브젝트의 속성을 변경할 수 있습니다. (예를 들어, 로켓 Rigidbody의 velocity를 설정하는 등)

사용이 간단하다는 것과는 외에도, 나중에 프리팹을 변경할 수 있습니다. 따라서 로켓을 만드는 경우, 즉시 트레일 파티클(Particle trail)을 추가할 필요없이 나중에 추가 할 수 있습니다. 트레일을 자식 게임 오브젝트로 프리팹에 추가하면 모든 인스턴스화 된 로켓은 트레일 파티클이 가능합니다. 그리고 마지막으로, 신속하게 인스펙터에서 로켓 프리팹의 프로퍼티를 조정할 수 있기 때문에 게임의 조정이 훨씬 쉬워집니다.

다음 스크립트 로켓을 Instantiate() 함수를 사용하여 발사하는 방법을 보여줍니다.

// JavaScript

// Require the rocket to be a rigidbody.
// This way we the user can not assign a prefab without rigidbody
var rocket : Rigidbody;
var speed = 10.0;

function FireRocket () {
    var rocketClone : Rigidbody = Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    // You can also acccess other components / scripts of the clone
    rocketClone.GetComponent.<MyRocketScript>().DoSomething();
}

// Calls the fire method when holding down ctrl or mouse
function Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
}


// C#

// Require the rocket to be a rigidbody.
// This way we the user can not assign a prefab without rigidbody
public Rigidbody rocket;
public float speed = 10f;

void FireRocket () {
    Rigidbody rocketClone = (Rigidbody) Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    
    // You can also acccess other components / scripts of the clone
    rocketClone.GetComponent<MyRocketScript>().DoSomething();
}

// Calls the fire method when holding down ctrl or mouse
void Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
}


캐릭터를 Ragdoll 또는 파괴물로 대체

완전히 리깅된 적 캐릭터가 있고, 사망했다고 가정합니다. 캐릭터를 단순히 사망한 애니메이션을 재생하여 모든 적 로직을 처리하고 있던 모든 스크립트를 비활성화합니다. 아마 몇 가지 스크립트를 제거할 필요가 생기고, 추가 커스텀 로직에 의해 모두 사망한 적을 공격하지 않는 등 기타 정리 작업이 필요합니다.

훨씬 더 좋은 방법은 적 캐릭터를 즉시 삭제하여 인스턴스화 된 파괴물로 바꾸는 것입니다. 그러면 유연성이 높아집니다. 사망 캐릭터에 대해 다른 메테리얼을 사용할 수 있고, 완전히 다른 스크립트를 연결하거나 여러 조각으로 파괴된 오브젝트를 포함한 프리팹을 생성하고 조각난 적을 재현하거나 또는 단순히 캐릭터가 있는 버전을 가진 프리팹를 인스턴스화 할 수도 있습니다.

어떤 옵션이라도 한번 Instantiate()를 호출하는 것만으로 올바른 프리팹으로 연결하면, 그걸로 완료됩니다.

기억해야 하는 중요한 부분은 Instantiate()하는 파괴물은 원래와는 완전히 다른 오브젝트로 만들 수 있다는 것입니다. 예를 들어, 비행기가 있다고 가정하고, 두 버전을 모델링합니다. 하나는 비행기가 Mesh Renderer와 비행기의 실제 움직임을 실행하는 스크립트입니다. 모델을 하나의 오브젝트에만 함으로써, 게임은 더 적은 삼각형으로 만들어지고 더 적은 오브젝트로 구성되므로 작은 부품을 많이 사용하는 것보다 렌더링이 일찍, 더 빠르게 가능합니다. 또한 비행기가 안정적으로 날아다니고 있는 동안은 일부러 몇 가지 부분으로 나눌 필요가 없습니다.

파괴된 비행기 프리팹을 빌드하기 위한 일반적인 단계는 다음과 같습니다 :

  1. 원하는 모델링 도구를 사용하여 비행기의 다른 부분들을 만듭니다.
  2. 빈 씬을 생성
  3. 빈 씬에 모델을 드래그 앤 드롭
  4. Rigidbody를 모든 부분을 선택하고 Component->Physics->Rigidbody를 선택하여 모든 부분에 추가
  5. Box Colliders를 모든 부분을 선택하고 Component->Physics->Box Collider을 선택하여 모든 부분에 추가
  6. 보다 특별한 효과를 위해, 연기와 같은 파티클 시스템을 각각의 자식 게임 오브젝트들 에 추가
  7. 이제 비행기에는 여러 부분으로 폭발하며, 지상에 물리 효과가 적용된 움직임으로 떨어지고, 연결된 파티클 시스템에 의한 트레일 파티클이 생성됩니다
  8. Assets->Create Prefab을 선택
  9. 모든 비행기의 부분들을 포함하는 루트의 게임 오브젝트를 프리팹으로 드래그 앤 드롭

다음 예제에서는 이러한 단계가 어떻게 코드에서 모델링되고 있는지를 보여드립니다.

// JavaScript

var wreck : GameObject;

// As an example, we turn the game object into a wreck after 3 seconds automatically
function Start () {
    yield WaitForSeconds(3);
    KillSelf();
}

// Calls the fire method when holding down ctrl or mouse
function KillSelf () {
    // Instantiate the wreck game object at the same position we are at
    var wreckClone = Instantiate(wreck, transform.position, transform.rotation);

    // Sometimes we need to carry over some variables from this object
    // to the wreck
    wreckClone.GetComponent.<MyScript>().someVariable = GetComponent.<MyScript>().someVariable;

    // Kill ourselves
    Destroy(gameObject);


// C#

public GameObject wreck;

// As an example, we turn the game object into a wreck after 3 seconds automatically
IEnumerator Start() {
    yield return new WaitForSeconds(3);
    KillSelf();
}

// Calls the fire method when holding down ctrl or mouse
void KillSelf () {
    // Instantiate the wreck game object at the same position we are at
    GameObject wreckClone = (GameObject) Instantiate(wreck, transform.position, transform.rotation);
    
    // Sometimes we need to carry over some variables from this object
    // to the wreck
    wreckClone.GetComponent<MyScript>().someVariable = GetComponent<MyScript>().someVariable;
    
    // Kill ourselves
    Destroy(gameObject);
}

}

오브젝트들을 특정 패턴으로 배치

예를 들어 많은 오브젝트를 격자 모양 또는 원형으로 배치하고 싶다고 가정합니다. 전통적으로 이것은 다음 방법 중 하나를 취합니다 :

  1. 코드에서 완전히 오브젝트를 만든다고 합시다. 이것은 지루한 작업입니다. 스크립트에서 값을 입력하는 것은 느리고, 직관적이지 않고, 번거롭습니다.
  2. 완전히 리깅된 오브젝트를 만들고 복제하여 씬에 여러 번 배치합니다. 이것은 지루한 작업으로, 격자(Grid)에 정확하게 배치하는 것은 어려운 작업입니다.

따라서 대신 프리팹으로 Instantiate()을 사용하십시오. 이 시나리오에서 왜 프리팹 사용이 유용한지 알았다고 상상합시다. 이 시나리오에 필요한 코드는 다음과 같습니다 :

// JavaScript

// Instantiates a prefab in a circle

var prefab : GameObject;
var numberOfObjects = 20;
var radius = 5;

function Start () {
    for (var i = 0; i < numberOfObjects; i++) {
        var angle = i * Mathf.PI * 2 / numberOfObjects;
        var pos = Vector3 (Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
        Instantiate(prefab, pos, Quaternion.identity);
    }
}


// C#
// Instantiates a prefab in a circle

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;
        Vector3 pos = new Vector3(Mathf.Cos(angle), 0, Mathf.Sin(angle)) * radius;
        Instantiate(prefab, pos, Quaternion.identity);
    }
}


// JavaScript

// Instantiates a prefab in a grid

var prefab : GameObject;
var gridX = 5;
var gridY = 5;
var spacing = 2.0;

function Start () {
    for (var y = 0; y < gridY; y++) {
        for (var x=0;x<gridX;x++) {
            var pos = Vector3 (x, 0, y) * spacing;
            Instantiate(prefab, pos, Quaternion.identity);
        }
    }
}


// C#

// Instantiates a prefab in a grid

public GameObject prefab;
public float gridX = 5f;
public float gridY = 5f;
public float spacing = 2f;

void Start() {
    for (int y = 0; y < gridY; y++) {
        for (int x = 0; x < gridX; x++) {
            Vector3 pos = new Vector3(x, 0, y) * spacing;
            Instantiate(prefab, pos, Quaternion.identity);
        }
    }
} 


Saving Your Work
입력