Chapter 3. Prefab (+ Resource Manager)

Date:     Updated:

카테고리:

태그:

인프런에 있는 Rookiss님의 [C#과 유니티로 만드는 MMORPG 게임 개발 시리즈] Part3: 유니티 엔진 강의를 듣고 정리한 필기입니다. 😀
🌜 강의 들으러 가기 Click

🚖 프리팹

프리팹 정의와 생성

오브젝트를 에셋 파일로 만들어 두는 것을 Prefab 이라고 한다. 만들어 둔 프리팹을 CLone 화 하여 씬에 실존하는 오브젝트로 찍어낼 수 있다.

프리팹을 이용하여 게임 도중에도 실시간으로, 프리팹으로 오브젝트를 씬 상에서 실존하도록 찍어내어 생성할 수 있고, 프리팹 하나로 수 많은 동일한 오브젝트들을 동시에 생성할 수도 있다. Class(프리팹) 를 만들어 두고 이럴 객체 인스턴스(오브젝트)로 찍어내는 것과 같다. 프리팹은 오브젝트와 다르게 게임 상에 실존하는게 아니라 오브젝트를 찍어내는 틀이라고 생각하면 된다.

  • 프리팹 만드는 방법
    • 그냥 프리팹화 하려는 오브젝트를 📂Assets 에 드래그 앤 드롭 하면 된다.
  • 프리팹을 Scene에 오브젝트로서 찍어내기 (Clone 만들기)
    • 마찬가지로 Hierarchy 혹은 Scene 창에 프리팹을 드래그 앤 드롭하면 된다.
    • 코드 상으로는 Instantiate 함수를 사용하여 프리팹을 오브젝트로 실존하게 찍어낼 수 있다.
  • 프리팹 수정하는 방법
    • 수정하려는 프리팹 더블 클릭. 혹은 Open Prefab 누르면 해당 프리팹을 수정할 수 있는 모드가 나온다. (오브젝트로서가 아닌 프리팹으로서 수정한다.) image
    • 아래와 같이 프리팹으로 만든 오브젝트를 통해 프리팹을 수정하려면 Hierarchy 에서 해당 오브젝트 옆에 조그만 화살표를 누르면 프리팹 모드로 갈 수 있다!

      image


프리팹 오버라이딩

Override 👉 덮어 쓰다. 재정의 하다.

image

프리팹으로 Cube, Cube(1), Cube(2) 이렇게 3 개의 오브젝트를 찍어낸 상태이다. 그러니 셋 다 프리팹에 설정되어 있는 기본값을 따른다. 그러나 Cube(1)의 Box Collider 컴포넌트를 Cube(1)만 좀 수정해보니! 이렇게 Cube(1)의 인스펙터의 Overrides 버튼을 눌러 보면 프리팹과 다르게 어떤 부분이 달라졌는지, 즉 어떤 부분이 오버라이딩 되었는지가 나타난다.

image

오버라이딩 된(기존 프리팹과 다르게 재정의 된) 부분을 클릭하면 이렇게 어떻게 달라졌는지 친절하게 나온다! 프리팹에선 박스 콜라이더의 사이즈가 1, 1, 1 이었는데 Cube(1)의 박스 콜라이더는 사이즈가 3, 3, 3 으로 오버라이딩 됐다는 것을 볼 수 있다.

  • Revert All 👉 다시 프리팹에서 설정된 기본값들로 되돌림
  • Apply All 👉 프리팹에도, 모든 오브젝트(같은 프리팹으로 찍어낸)들에게도 이렇게 재정의한 내용을 다 적용 시킴.

프리팹은 프리팹과 동일한 속성과 값을 가진 여러개의 오브젝트로 찍어낼 수도 있지만 또 이렇게 찍어낸 각각의 오브젝트들도 다르게 개성있게 재정의할 수 있다는 장점이 있다!


Nested Prefab

프리팹 속 프리팹

프리팹을 구성하고 있는 요소에 또 프리팹이 있는 경우다.

image

Prefab Variant

상속 개념이다. 👉 다 물려 받되, 자신만의 새로운 기능 추가.

image

  • 이미 프리팹으로 만들어진 오브젝트를 가지고 다시 프리팹으로 만드려고 한다면 위와 같은 다이얼로그가 뜬다.
    • Original Prefab
      • 지금 자기 자신의 모습 그대로를 바탕으로 별개의 새로운 프리팹을 만든다.
    • Prefab Variant
      • 자기 자신을 찍어냈던 그 프리팹을 상속받는 새로운 프리팹으로서 만든다.
      • 추후 오버라이드도 할 수 있고 새로운 기능도 덧붙일 수 있되, 기존의 프리팹 성질은 다 물려 받는 새로운 자식 프리팹을 생성하는 것과 마찬가지다.

image


🚖 프리팹을 인스턴스로 찍어내는 방법

유니티 에디터에서 직접 할당하기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrefabTest : MonoBehaviour
{
    public GameObject prefab;

    void Start()
    {
        Instantiate(prefab);
    }
}

1️⃣ public GameObject prefab;

프리팹을 할당할 수 있는 GameObject 타입의 변수를 선언하고 유니티 에디터에서 변수에 직접 프리팹을 할당해준 후 Instantiate 함수의 인수로 넘겨주는 방법.

  • Instantiate
    • 인수로 넘겨 받은 프리팹을 인스턴스(실존하는 게임 오브젝트)화 한다.
    • 그리고 찍어낸 그 게임 오브젝트를 리턴한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrefabTest : MonoBehaviour
{
    public GameObject prefab;

    GameObject tank;

    void Start()
    {
        tank = Instantiate(prefab);

        Destroy(tank, 3.0f);
    }
}

tank에 프리팹으로 찍어낸 오브젝틀르 받아 놓고 이를 사용해 Destroy 에 넘겨 3 초후에 삭제!


코드 상으로 Resources 사용하여 프리팹 지정

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrefabTest : MonoBehaviour
{
    private GameObject prefab;

    GameObject tank;

    void Start()
    {
        prefab = Resources.Load<GameObject>("Prefabs/Tank");
        tank = Instantiate(prefab);

        Destroy(tank, 3.0f);
    }
}

image

2️⃣ Resources.Load<GameObject>(“Prefabs/Tank”);

  • **Resources.Load<에셋타입>(string에셋경로)**
    • 불러오려는 에셋의 타입과 에셋의 경로를 지정해주면 해당 에셋을 로컬 환경(📂Resources)에서 찾아 GameObject로서 불러오고 이를 리턴하는 함수다.
    • 📂Assets/Resources 로컬 폴더에서 에셋을 가져온다.
      • 그러니 위의 예에선 📂Assets/Resources/Prefabs 폴더에 있는 “Tank” 프리팹을 가져오게 되는 것이다.
      • 📂Resources가 없다면 만들어주자.
  • 프리팹을 직접 유니티 에디터에서 일일이 변수에 할당해줄 필요 없이 게임 중에 코드상으로 불러올 에셋이 있다면 📂Resources 폴더 안에 넣어두고 Resources.Load 함수를 ㅅ ㅏ용하자.


📜ResourceManager.cs

여러 스크립트 내에서 Resources.Load 함수를 사용하여 에셋을 불러오거나 아니면 직접 변수에 에셋을 할당하는 식으로 하면, 여러 곳에서 불러오는 작업을 하게 되므로 잘못되도 추적이 어렵고 수정도 어려워 진다.

오로지 에셋을 불러오는 작업만 하는 스크립트로 따로 만들었다. Input Manager 처럼!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class ResourceManager 
{
    public T Load<T>(string path) where T : Object
    {
        return Resources.Load<T>(path);
    }

    public GameObject Instantiate(string path, Transform parent = null)
    {
        GameObject prefab = Load<GameObject>($"Prefabs/{path}");
        if (prefab == null)
        {
            Debug.Log($"Filed to load prefab : {path}");
            return null;
        }

        return Object.Instantiate(prefab, parent);
    }

    public void Destroy(GameObject go)
    {
        if (go == null)
            return;

        Object.Destroy(go);
    }
}

  • Load 제네릭 사용자 지정 함수 정의
    • where T : Object 👉 부모 클래스가 Object 인 타입만 받을 수 있도록 제약을 걸음 (C# 문법)
    • Resources.Load<T>(path)
      • 👉 📂Resource 폴더를 시작 위치로 한 “path”에 해당하는 T 타입의 에셋 파일을 불러오고 이를 리턴한다.
  • Instantiate 사용자 지정 함수 정의
    • Load 를 사용해 prefab에 path 에 해당하는 GameObject 타입의 에셋을 할당한다.
      • 📂Resource의 📂Prefab 에서 찾아 온다. ($”Prefabs/{path}”)
      • 성공적으로 찾았다면 Object.Instantiate(prefab, parent) 리턴
        • ✨그냥 Instantiate이 아닌 Object.Instantiate 이라고 명시해준 이유 ✨
          • 그냥 Instantiate 라고 명시하면 지금 정의하고 있는 이 사용자 지정 함수 Instantiate 라고 인식되서 재귀호출 되므로.
      • 못 찾았다면 null 리턴
  • Destroy 사용자 지정 함수 정의
    • 마찬가지로 재귀를 막기 위해 Object. 까지 붙여서 Object.Destroy 호출


📜Manager.cs

이전 포스트에 다루었던 게임 전반적인 부분을 총관리하는 싱글톤이다. 마찬가지로 📜Resource Manager도 📜Manager.cs 의 멤버로 두어(구성 요소로 두어) 싱글톤으로 관리하자.

ResourceManager _resource = new ResourceManager();
public static ResourceManager Resource { get { return Instance._resource; } }
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Managers : MonoBehaviour
{
    static Managers s_instace;  
    static Managers Instance { get { Init(); return s_instace; } }

    InputManager _input = new InputManager();
    ResourceManager _resource = new ResourceManager();
    
    public static InputManager Input { get { return Instance._input; } }
    public static ResourceManager Resource { get { return Instance._resource; } }
    

    void Start()
    {
        Init();
    }

    void Update()
    {
        _input.OnUpdate();
    }

    static void Init()
    {
        if (s_instace == null)
        {
            GameObject obj = GameObject.Find("@Managers");
            if (obj == null)
            {
                obj = new GameObject { name = "@Managers" };
                obj.AddComponent<Managers>();
            }

            DontDestroyOnLoad(obj);
            s_instace = obj.GetComponent<Managers>();
        }
    }
}


📜ResourceManager.cs 사용

Managers.Resource.Instantiate("Tank");
Managers.Resource.Destroy(tank);
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PrefabTest : MonoBehaviour
{
    private GameObject prefab;

    GameObject tank;

    void Start()
    {
        Managers.Resource.Instantiate("Tank");
        Managers.Resource.Destroy(tank);
    }
}



🌜 개인 공부 기록용 블로그입니다. 오류나 틀린 부분이 있을 경우 
언제든지 댓글 혹은 메일로 지적해주시면 감사하겠습니다! 😄

맨 위로 이동하기

Unity Lesson 2 카테고리 내 다른 글 보러가기

댓글 남기기