블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2794)
Unity3D (852)
Programming (478)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (11)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

런타임tl 프리팹을 인스턴스화 하기 Instantiating Prefabs at runtime

이 시점에서 사용자는 프리팹 Prefabs 에 관한 기초적인 개념을 이해하고 있어야 합니다. 프리팹은 사용자의게임을 통털어 재사용이 가능한 미리 설정된 게임오브젝트 GameObjects들과 컴포넌트 Components들의 집합입니다. 프리팹이 무엇인지 모르실 경우, 기초 지식을 위해Prefabs 페이지를 읽어보시길 권유합니다.

프리팹은 복잡한 게임오브젝트를 런타임때 인스턴스화(instantiate) 시키길 원하는 경우 매우 유용합니다. 프리팹을 인스턴스화 하는 것 외에 다른 방안은 코드를 사용해 처음부터 게임오브젝트를 만드는 것입니다. 프리팹 인스턴스화는 이런 대안에 비하여 더 많은 장점들을 가지고 있습니다:

  • 사용자는 한 줄의 코드로 완전한 기능을 가진 프리팹의 인스턴스화를 할 수 있습니다. 똑같은 게임오브젝트를 코드로 처음부터 만드는 것은 평균 5줄 혹은 더 이상이 필요하게 됩니다.
  • 사용자는 씬 Scene과 인스펙터 Inspector 에서 프리팹을 더 빠르고 쉽게 설정하고 테스트하고 수정할 수 있습니다.
  • 사용자는 인스턴스 되어진 프리팹을 그것을 인스턴스화하는 코드를 변경하지 않고도 해당 프리팹을 변경할 수 있습니다. 가령 코드 변경없이도 일반 로켓탄을 슈퍼 충전된 로켓으로 변경할 수 있습니다.

일반적인 시나리오들 Common Scenarios

프리팹의 강점을 설명하기 위해서, 프리팹이 유용하게 쓰여지는 기본적인 상황들을 고려해 보겠습니다:

  1. 벽돌 프리팹을 각각 다른 위치로 여러 차례 생성하여 벽을 만들수 있습니다.
  2. 로켓발사기는 로켓을 쏘았을 때 날으는 로켓탄 프리팹을 인스턴스화 합니다. 그 프리팹은 메쉬, 강체Rigidbody충돌체 Collider, 그리고, 자신의 흔적 trail 파티클 시스템 Particle System을 가진 자식 child 게임오브젝트를 포함합니다.
  3. 많은 조각들로 폭발하는 로봇. 완전하게 작동하던 로봇은 부서진후 파괴된_로봇_프리팹 으로 교체됩니다. 이 프리팹들은 각각의 강체 Rigidbodies 들과 파티클 시스템들이 설정된 여러 개의 파트들로 나눠진 로봇으로 구성될 것입니다. 이 테크닉은 로봇을 많은 조각으로 폭발시키며 한 개의 객체를 한 개의 프리팹으로 교체하는 것을 단 한 줄의 코드로 할 수 있게 해줍니다.

벽 짓기 Building a wall

아래의 설명은 프리팹을 사용하는 것과 코드로 객체를 생성하는 것의 비교를 통하여 프리팹 사용의 장점을 보여줄 것입니다.

우선, 코드로부터 벽돌을 만들어 봅니다:

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);
        }
    }
} 
  • 위의 스크립트를 사용하기 위해서는, 사용자는 단순히 스크립트를 저장하고 비어있는 게임오브젝트로 스크립트를 드래그 합니다.
  • GameObject->Create Empty로 빈 게임오브젝트를 생성합니다.

만약 위의 코드를 실행하면, 사용자가 플레이 모드 Play Mode 로 들어갈 때 벽돌 벽 전체를 보게 될 것입니다. 거기에는 각각의 벽돌의 기능에 관련된 두 개의 코드 줄이 있습니다. 이들은 CreatePrimitive()와 AddComponent()입니다. 지금 이것도 나쁘지는 않지만, 벽돌들은 텍스쳐를 가지고 있지 않습니다. 텍스쳐 texture, 마찰력 friction, 강체의 질량 Rigidbody mass을 바꾸는 것처럼 벽돌에 행해지는 각각의 추가적인 행동들은 모두 추가적인 줄을 필요로 하게 됩니다.

만약 사용자가 프리팹을 생성하여, 모든 설정을 사전에 해놓는다면, 각 벽돌의 설정과 생성을 수행하기 위해서 한 줄의 코드만 사용하게 됩니다. 이것은 사용자가 무언가 수정을 해야한다고 느꼈을 때 많은 코드들을 관리하고 수정하는 수고를 덜어 줄 것 입니다. 프리팹을 사용하면 사용자는 단지 수정한 후 플레이하면 됩니다. 코드의 수정이 따로 필요 없습니다.

개개의 벽돌을 위해 프리팹을 사용할 것이라면, 다음이 사용자가 벽을 생성하기 위해 필요한 코드입니다.

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

위의 코드는 아주 깔끔할 뿐만 아니라 재사용이 가능합니다. 여기에는 큐브를 인스턴스화한다 instantiating 라던지 큐브가 강체 rigidbody 를 포함 해야 한다 라는 말이 필요 없습니다. 이 모든 것은 프리팹에서 정의되고 에디터에서 빠르게 생성될 수 있습니다.

이제 사용자는 에디터 안에서 프리팹을 만들기만 하면 됩니다. 다음과 같은 방법으로 프리팹을 생성합니다:

  1. GameObject->Create Other->Cube를 선택합니다.
  2. Component->Physics->Rigidbody를 선택합니다.
  3. Assets->Create Prefab를 선택합니다.
  4. 프로젝트 뷰 Project View에서, 새로운 프리팹의 이름을 "Brick"으로 바꿉니다.
  5. 계층 Hierarchy에서 생성한 큐브를 드래그하여 프로젝트 뷰의 “Brick” 프리팹에 놓습니다.
  6. 생성된 프리팹으로 사용자는 계층에서 안전하게 큐브를 삭제할 수 있습니다 (윈도우에서는 Delete, 맥에서는 Command-Backspace)

이제 벽돌 프리팹이 생성되었습니다. 이제 사용자는 이것을 스크립트의 cube 변수에 붙여야 attach 합니다. 스크립트를 포함하는 빈 게임오브젝트를 선택하세요. 인스펙터에서 새로운 변수인 "cube"가 나타난 것을 보게 될 것입니다.


이 변수는 어떤 게임오브젝트나 프리팹도 받아들일 수 있습니다

이제 "Brick" 프리팹을 프로젝트 뷰에서 인스펙터의 cube 변수위로 드래그 합니다. 플레이 Play 를 누르면 프리팹을 사용하는 벽을 보게 될 것입니다.

이것은 유니티에서 굉장히 많이 사용되는 작업방식 패턴입니다. 사용자는 처음에 코드로부터 큐브를 생성하는 스크립트가 단 2줄이 더 긴데 왜 이 방식이 훨씬 더 낫다는 건지 의아해 할 수도 있습니다.

그러나 지금은 프리팹 사용으로 인해 단 몇 초 내에 프리팹을 수정할 수 있습니다. 모든 인스턴스의 질량 수정이 필요합니까? 그렇다면 해당 프리팹 안의 강체를 수정하세요. 모든 인스턴스들에 각각 다른 재질 Material을 사용하기 원합니까? 그렇다면 해당 프리팹으로 재질을 한번만 드래그 해놓으면 됩니다. 마찰의 수정을 원하십니까? 그렇다면 프리팹의 충돌체에 다른 피직 재질 Physic Material을 사용하십시오. 여기 모든 박스에 파티클 시스템을 추가 하시겠습니까? 그렇다면 프리팹에 자식 child 을 한 번만 추가하시면 됩니다.

로켓과 폭발의 인스턴스화 Instantiating rockets & explosions

다음은 프리팹이 어떻게 이 시나리오에 들어맞는지에 대한 설명입니다:

  1. 로켓 발사기는 사용자가 로켓을 발사할 때, 로켓 프리팹을 인스턴스화 instantiate 합니다. 이 프리팹은 메쉬, 강체, 충돌체, 그리고 흔적 파티클 시스템 trail particle system 을 포함하는 자식 게임오브젝트를 가지고 있습니다.
  2. 로켓은 폭발 프리팹에 영향을 미치고 인스턴스화 시킵니다. 폭발 프리팹은 시간이 지나며 점점 희미해지는 빛의 파티클 시스템과 주위의 게임오브젝트들에 데미지를 가하는 스크립트를 지니고 있습니다.

로켓 게임오브젝트를 코드로 처음부터 작성하여 수동으로 컴포넌트 추가하고 속성 설정하는 것도 가능하지만 프리팹을 인스턴스화 하는 것이 훨씬 더 쉽습니다. 로켓의 프리팹이 얼마나 복잡하던지 간에 로켓을 한 줄의 코드로 로켓을 인스턴스화 할 수 있습니다. 프리팹을 인스턴스화 한 후에 사용자는 인스턴스화한 객체의 어떤 속성도 변경이 가능합니다 (예: 로켓 강체의 속도 설정).

사용이 쉬운 것 이외에도 프리팹은 나중에 업데이트가 가능하다는 장점이 있습니다. 가령 사용자가 로켓을 만든다면, 로켓에 당장 흔적 파티클을 추가할 필요가 없습니다. 나중에 업데이트 하는 것이 가능하기 때문입니다. 나중에 사용자가 자식 게임오브젝트로서 흔적 trail 을 프리팹에 추가하면, 사용자의 인스턴스 instance 화 된 모든 로켓들은 흔적 파티클들을 가지게 될 것입니다. 그리고 마지막으로 사용자는 인스펙터의 로켓 프리팹의 속성들을 빨리 수정해서 게임을 정교하게 가다듬는 것을 더 쉽게 만들어 줍니다.

이 스크립트는 Instantiate() 함수를 사용하여 어떻게 로켓을 발사하는지 보여줍니다.

// 로켓이 강체가 되어야 합니다.
// 그래야 사용자가 강체없이 프리팹 할당을 못합니다.
var rocket : Rigidbody;
var speed = 10.0;

function FireRocket () {
    var rocketClone : Rigidbody = Instantiate(rocket, transform.position, transform.rotation);
    rocketClone.velocity = transform.forward * speed;
    // 사용자는 복제품의 컴포넌트나 스크립트에 접근할 수 있습니다.
    rocketClone.GetComponent(MyRocketScript).DoSomething();
}

//CTRL나 마우스를 누르고 있을때 발사 메쏘드를 호출합니다.
function Update () {
    if (Input.GetButtonDown("Fire1")) {
        FireRocket();
    }
} 

캐릭터를 랙돌 ragdoll 이나 망가진 것으로 교체하기 Replacing a character with a ragdoll or wreck

만약 사용자가 완전히 리깅된 적 캐릭터를 가지고 있는데 그가 죽었다고 가정해 보세요. 이 경우 사용자는 단순히 캐릭터에 죽음 애니메이션이 작동하게 하고 그 적 캐릭터의 로직을 다루는 모든 스크립트를 사용불가 disable 상태로 만들수도 있습니다. 이때 사용자는 몇몇 스크립트들을 제거하고, 다른 아무도 죽은 적을 더 이상 공격하지 않는 몇몇 커스텀 로직을 더해야 하는 등등의 여러가지 청소 작업에 신경 써야 할 것입니다.

하지만 이보다 더 좋은 접근법은 캐릭터를 통체로 즉시 삭제하고, 그 캐릭터를 인스턴스화된 망가진 프리팹 인스턴스로 교체하는 것입니다. 이는 사용자에게 더 많은 유연성을 제공해 줍니다. 죽은 캐릭터를 위해서 다른 재질 을 사용할 수도 있고, 완전히 다른 스크립트들을 붙일 수도 있을 것이며, 산산조각난 적을 연출하기 위해 여러 개로 쪼개진 그 객체를 포함하는 프리팹을 만들거나 혹은 단순히 그 캐릭터의 버전을 포함하는 프리팹을 인스턴스 시킬 수도 있을 것입니다.

이 어떤 선택사항들도 Instantiate()로 한번의 호출로 가능하며, 사용자는 간단히 그것을 맞는 프리팹으로 연결만하면 완료가 되는 것입니다!

이 중 꼭 기억해야 할 중요한 파트은 사용자가 Instantiate() 명령어를 사용해 만든 부서진 캐릭터를 원래와 완전히 다른 객체들로 구성할 수 있다는 점입니다. 예를 들어 사용자가 비행기가 있을시 사용자는 두 가지 버전으로 그것을 모델링 할 것입니다. 비행기가 메쉬 렌더러 Mesh Renderer와 비행기 피직스 (물리) physics 를 위한 스크립트들을 가진 하나의 게임오브젝트로 구성된 모델의 경우를 봅니다. 이 모델을 하나의 게임오브젝트에 간직함으로 인하여, 사용자는 더 적은 수의 삼각형들로 구성된 모델로 만들 수 있으므로 사용자의 게임 속도는 더욱 더 향상될 것입니다. 그리고 더 적은 수의 객체들로 구성됨으로 인해, 여러개의 작은 파트들을 많이 사용하는 경우에 비해 렌더 속도도 더 빠를 것입니다. 또한 비행기에 문제없이 날고 있다면, 굳이 분리된 파트들로 구성된 모델을 가지고 있을 이유가 없습니다.

망가진 비행기 프리팹을 만들기 위한 일반적인 단계는 다음과 같습니다:

  1. 사용자가 선호하는 3D 모델링 프로그램으로 많은 파트들로 구성된 비행기를 모델링합니다.
  2. 비어있는 씬 empty Scene 을 생성합니다.
  3. 모델을 빈 씬으로 드래그 합니다.
  4. 모든 파트들을 선택하고 Component->Physics->Rigidbody 메뉴를 선택해서 강체 Rigidbodies 를 모든 파트들에 더합니다.
  5. 모든 파트들을 선택하고 Component->Physics->Box Collider를 선택해서 박스 충돌체를 모든 파트들에 더합니다.
  6. 추가적인 특수 효과를 위해서 각 파트들에 연기와 같은 파티클 시스템을 자식 child 게임오브젝트로써 더합니다.
  7. 이제 사용자는 복수개의 폭발된 파트들로 구성된 비행기를 가지고 있고 이것들은 피직스 physic 에 의해 땅으로 떨어지고 부착된 파티클 시스템으로 인하여 흔적 파티클들을 만들어 낼 것입니다. 재생 play 을 눌러 모델이 어떤 식으로 반응하는지 살펴본 후 필요한 변경을 가해 주세요.
  8. Assets->Create Prefab를 선택합니다.
  9. 비행기 파트들을 모두 포함한 루트 root 게임오브젝트를 프리팹 안으로 드래그 합니다.
var wreck : GameObject;

//한 예로 우리는 3초 후에 자동적으로 게임오브젝트가 망가진 것으로 바뀌도록 해봅니다.
function Start () {
    yield WaitForSeconds(3);
    KillSelf();
}

// CTRL이나 마우스를 누를때 발사 메쏘드를 호출합니다.
function KillSelf () {
    // 우리가 있는 같은 위치로 부서진 게임오브젝트의 인스턴스를 생성합니다.
    var wreckClone = Instantiate(wreck, transform.position, transform.rotation);

    // 때로 우리는 이 객체로부터 몇몇 변수들을 가져올 필요가 있습니다.
    // 부서진 게임오브젝트로 말입니다.
    wreckClone.GetComponent(MyScript).someVariable = GetComponent(MyScript).someVariable;

    // 우리를 파괴합니다.
    Destroy(gameObject);
} 

일인칭 슈팅 게임 튜토리얼(강좌)은 캐릭터를 랙돌 ragdoll 버젼으로 어떻게 바꾸고 애니메이션의 마지막 상태로 팔다리를 동기화하여 맞추는지 설명합니다. 이 강좌는 Tutorials 페이지에서 찾으실 수 있습니다.

여러 개의 객체들을 특정한 패턴으로 배치하기 Placing a bunch of objects in a specific pattern

사용자가 다양한 오브젝트들을 그리드나 원형의 패턴에 놓고 싶어한다고 합시다. 예전에는 이것들을 다음과 같은 방법들로 실현했습니다:

  1. 완전히 코드만으로 객체를 만듭니다. 이는 굉장히 지루합니다. 한 스크립트에 값들을 넣는 것은 작업속도가 느려질 뿐만 아니라 직관적이지도 않아 힘만 들게 됩니다.
  2. 완전히 리깅된 오브젝트를 만들고, 그것을 복제해서 씬 Scene 의 여러 곳에 배치하는 방법입니다. 이 또한 지루한 작업이며, 객체들을 그리드 (좌표) grid 에 정확하게 놓는 것은 어렵습니다.

그러므로, 프리팹과 Instantiate() 를 이용하세요! 이러한 시나리오들에서 왜 프리팹들이 유용한지 이제는 이해가 되실 것입니다. 이 시나리오들을 위해 필요한 코드는 다음과 같습니다:

// 원 안에서 프리팹 인스턴스들 만들기

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);
    }
} 
//그리드 안에 프리팹 인스턴스들 만들기

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);
        }
    }
} 



반응형

'Unity3D' 카테고리의 다른 글

빌보드 관련  (0) 2012.11.02
Unity3D로 만드는 초간단 2D 게임 (1)  (0) 2012.11.02
안드로이드 빌드 셋팅하기  (22) 2012.10.25
MonoDevelop에서 디버깅하기  (0) 2012.10.25
Inspector에 다차원 배열 값 셋팅하기(for C#)  (1) 2012.10.24
Posted by blueasa
, |