C#/수업내용

2020.08.06. 수업내용 - Asset Bundle

dev_sr 2020. 8. 6. 14:28

CDN 서버 (Content Delivery Network)

-데이터를 전달하는 서버

-사용자가 몰릴 때 분산 처리를 해줌

 

Asset Bundle

-게임 안의 리소스들을 빌드할 때 포함하지 않고 외부로부터 ( CDN 서버를 사용해서 ) 다운받게 해준다

-패치를 위해 사용 ( 스토어에서 앱을 다시 다운받지 않게 해줌 -> 필요한 리소스만 다운 )

-Unity 안의 모든 것을 Asset Bundle로 묶을 수 있음

-character, weapon, monster.. 등 단위별로 나누고 한 단위씩 다운받음

-클라이언트가 받으면 메모리에 올라가는 데 그걸 Instantiate 하고 메모리에서 삭제함

 

 

 

에셋 번들 - Unity 매뉴얼

에셋 번들(AssetBundle) 은 플랫폼 특정 에셋(모델, 텍스처, 프리팹, 오디오 클립 , 씬 전체)이 들어있는 아카이브 파일로 실행 시에 로드할 수 있습니다. 에셋 번들에는 서로 종속성을 가질 수 있는��

docs.unity3d.com

 

프리팹을 만들고 AssetBundle을 characters 로 설정해준다

 

Editor폴더를 만든다.

Editor 폴더는 어디에 있어도 상관없다 (무조건 E로 시작)

 

CreateAssetBundle 스크립트를 작성한다

Editor에 넣는다.

 

 

Monobehavior는 상속받지 않는다.

UnityEditor를 사용하면 unity 자체를 커스터마이징 할 수 있다. 

using UnityEngine;
using UnityEditor;
using System.IO;

public class CreateAssetBundle 
{

   [MenuItem("Assets/Build AssetBundles")]   //유니티 상단 메뉴 Assets하단에 이 기능을 추가하겠다.
   static void BuildAllAssetBundles()   //플레이 하지 않아도 Build AssetBundles 메뉴를 누르기만 하면 실행됨
    {
        Debug.Log("Assets/Build AssetBundles");
        string assetBundlerDir = "Assets/AssetBundles";

        if(!Directory.Exists(assetBundlerDir))  //없으면 만든다
        {
            Directory.CreateDirectory(assetBundlerDir);
        }

        BuildPipeline.BuildAssetBundles(assetBundlerDir, BuildAssetBundleOptions.None, BuildTarget.StandaloneWindows);
    }
}

 

유니티 상단 Asset메뉴에 Build AssetBundles가 생기는데

누르면

 

AssetBundle 폴더가 생기고 밑에 assetbundle 이름마다 파일이 생긴다.

 

assetbundle을 path로 찾아서 사용할 수 있다.

assetbundle을 만든 뒤 prefab등이 변경되면 다시 만들어줘야 적용이 됨..

 

 

메모리로 불러오기

무기도 불러와서 손에 쥐고 있다.

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

public class LoadAssetBundlesFromMemory : MonoBehaviour
{
    void Start()
    {
        var path = "Assets/AssetBundles/characters";
        var path2 = "Assets/AssetBundles/weapons";
        StartCoroutine(this.LoadFromMemoryAsyc(path,path2));

    }

    private IEnumerator LoadFromMemoryAsyc(string path, string path2)
    {
        var data = File.ReadAllBytes(path);
        AssetBundleCreateRequest req = AssetBundle.LoadFromMemoryAsync(data);
        yield return req; //기다림

        var data2 = File.ReadAllBytes(path2);
        AssetBundleCreateRequest req2 = AssetBundle.LoadFromMemoryAsync(data2);
        yield return req2; //기다림


        //메모리에 올라가서 번들에 접근 가능
        AssetBundle bundle = req.assetBundle;
        var prefab = bundle.LoadAsset<GameObject>("ch_01_01");
        var model = Instantiate(prefab);
        Transform dummyRHand = GameObject.Find("DummyRHand").transform;
        Debug.Log(dummyRHand);

        AssetBundle bundle2 = req2.assetBundle;
        var weaponPrefab = bundle2.LoadAsset<GameObject>("Axe_14");
        var weaponModel = Instantiate(weaponPrefab);
        weaponModel.transform.SetParent(dummyRHand, false);


    }

}

 

 

 

파일로 불러오기

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

public class LoadAssetBundleFromFile : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        string path = Path.Combine(Application.dataPath + "/AssetBundles", "characters");
        AssetBundle bundle = AssetBundle.LoadFromFile(path);

        if(bundle == null)
        {
            Debug.Log("어셋 번들 로드 실패");
            return;
        }

        var prefab = bundle.LoadAsset<GameObject>("ch_01_01");
        var model = Instantiate(prefab);
    }

    
}

 

 

WebRequest  로 불러오기 

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

public class LoadAssetBundlesFromUnityWebRequest : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(this.LoadAssetBundle());
    }

    private IEnumerator LoadAssetBundle()
    {
        //var uri = "file:///" + Application.dataPath + "/AssetBundles/characters";
        var uri = "~AssetBundle이 있는 주소~/AssetBundles/characters";
        UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle(uri);

        yield return req.SendWebRequest();

        AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(req);
        var prefab = bundle.LoadAsset<GameObject>("ch_01_01");
        Instantiate(prefab);
    }
}

 

 

WebRequest로  AssetBundle 다 불러오기

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

public class LoadAllAssetBundles : MonoBehaviour
{
    void Start()
    {
        this.StartCoroutine(this.LoadAssetBundle());
    }

    private IEnumerator LoadAssetBundle()
    {
        var characterUri = "file:///" + Application.dataPath + "/AssetBundles/characters";
        var characterReq = UnityWebRequestAssetBundle.GetAssetBundle(characterUri);
        yield return characterReq.SendWebRequest();

        var characterBundle = DownloadHandlerAssetBundle.GetContent(characterReq);
        GameObject[] characterAssets = characterBundle.LoadAllAssets<GameObject>();

        int i = 0;
        Transform[] arrDummyRHand = new Transform[characterAssets.Length];
        foreach(var prefab in characterAssets)
        {
            var model = Instantiate(prefab);
            model.transform.position = new Vector3(i, 0, 0);
            var hero = model.gameObject.GetComponent<Hero>();
            //hero.Init();
            arrDummyRHand[i] = hero.TrdummyRHand;
            i++;
        }


        var weaponUri = "file:///" + Application.dataPath + "/AssetBundles/weapons";
        var weaponReq = UnityWebRequestAssetBundle.GetAssetBundle(weaponUri);
        yield return weaponReq.SendWebRequest();

        var weaponBundle = DownloadHandlerAssetBundle.GetContent(weaponReq);
        GameObject[] weaponAssets = weaponBundle.LoadAllAssets<GameObject>();

        int j = 0;
        foreach (var prefab in weaponAssets)
        {
            var model = Instantiate(prefab);
            model.transform.SetParent(arrDummyRHand[j],false);
            j++;
        }


    }

}

 

 

 

 

UnLoadTrue

로드된 에셋번들을 다시 언로드 한다.

안하거나 잘못하면 에셋이 누락되거나, 메모리 중복이 된다.

AssetBundle.Unload(bool) 로 호출

 

AssetBundle.Unload(true) : 로드된 모든 에셋 및 해당 종속성을 언로드. ( 껍데기만 남음-생성된 모든 인스턴스 제거 )

AssetBundle.Unload(false) : 로드된 모든 에셋을 에셋번들과의 연결을 끊고 에셋번들만 언로드.

 

3초 정도 시간을 두고 에셋 번들이 로드될 때까지 기다린 뒤 언로드 한다.

using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;
using UnityEngine.Networking;

public class UnLoadTrue : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        StartCoroutine(this.LoadAssetBundleUnloadFalse("characters"));
        StartCoroutine(this.LoadAssetBundleUnloadFalse("weapons"));
    }

    private IEnumerator LoadAssetBundleUnloadFalse(string bundleName)
    {
        var uri = string.Format( "에셋번들 있는 주소~~/AssetBundles/{0}",bundleName);
        var www = UnityWebRequestAssetBundle.GetAssetBundle(uri);
        yield return www.SendWebRequest();
        var bundle = DownloadHandlerAssetBundle.GetContent(www);
        GameObject[] prefabs = bundle.LoadAllAssets<GameObject>();

        foreach(var prefab in prefabs)
        {
            Instantiate(prefab);
        }

        yield return new WaitForSeconds(3);

        bundle.Unload(false);
    }
}