336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.

[Unity3D] Mac 에셋번들(asset bundle) 오류


Mac에서 에셋번들(asset bundle)을 만들고 파일질라(FileZila)를 통해서 업로드를 했는데


Error while downloading Asset Bundle: Failed to decompress data for the Assetbundle.. 


이런 오류가 나타났습니다.




잘 사용하고 있었던건데 금일 수정한것 때문에 그런것인지 당황했네요.


검색을 조금해보니 전송유형이 문제였습니다.


전송유형이 자동으로 되어있었습니다.



해결을 위해 전송유형을 바이너리(Binary)로 변경해주시면 됩니다.




쉽게 해결될 일이었는데 시간을 많이 소비했네요.


Error while downloading Asset Bundle: Failed to decompress... 


이런 문제로 당황스러운 분들은 참고하시면 좋을 것 같습니다.


336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.


유니티 버전 : 4.6.9


유니티 3d 4.x (Unity3d 4) 에셋번들 만들기 및 사용


이전에 유니티 5.x 에서 에셋번들을 만들었는데, 


유니티 4.x 에서 만드는 것도 포스팅합니다.



우선 씬에 다음처럼 큐브 하나를 만들겠습니다.


참고로 저는 미리 프리팹화 시켜서 Cube가 파란색입니다. (무시하세요)




리고 에셋번들을 만들 폴더를 Bundle이라는 이름으로 만들겠습니다.


그 하위로 A, B, C 폴더를 만들고


각 폴더안에 좀전에 만든 Cube를 프리팹 화 시키겠습니다.


정리하면 다음과 같습니다.


Bundle - A - Cube.prefab

           - B - Cube1.prefab

           - C - Cube2.prefab




에셋번들로 만들 항목들이 정해졌으니, 


이제 에디터를 하나 만들어 에셋번들로 만들겠습니다.


참고 : Creating Asset Bundlesin Unity 4


우선 편의를 위해 Editor 폴더를 만들고, 


BundleBuilder 라는 클래스를 하나 만들겠습니다.




자세한 코드 내용은 주석으로 설명하였습니다.


< BundleBuilder.cs 코드 >


using UnityEngine;
using System.Collections;
using UnityEditor;
using System.IO;

public class BundleBuilder : MonoBehaviour {

    [MenuItem("Assets/Build AssetBundle")]
    static void ExportResource()
    {
        // 저장할 에셋번들 이름.
        // 경로가 없을 경우 최상위(Assets상위)에 저장된다.
        // 현재 저장경로는 Assets/myAssetBundle 이다.
        string path = Application.dataPath + "/myAssetBundle";
        Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);

        // 현재 선택된 항목
        // 게임오브젝트만 해당.
        // 만약 씬에 있는 항목이라면, activeObject대신 activeTransform 사용권장.
        if(Selection.activeObject != null)
            Debug.Log("Current : " + Selection.activeObject.name);
        // 선택된 모든(하위 포함) 항목들
        foreach (Object sel in selection)
        {            
            Debug.Log("Path ; " + sel.ToString() + ", path :"+ AssetDatabase.GetAssetPath(sel));
        }

        // Selection.activeObject가 들어간 곳이, mainAsset이 된다.
        // 세번째 인자는 에셋번들을 저장할 경로(파일명 포함)
        // 현재 테스트로 Android 플랫폼으로 하도록 한다.
        BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path,
                                       BuildAssetBundleOptions.CollectDependencies
                                     | BuildAssetBundleOptions.CompleteAssets, BuildTarget.Android);
    }
}



코드를 작성하였다면, 저장을 해주세요.


그러면 다음처럼 메뉴가 생겼을 것입니다.


아까 만든 Bundle폴더를 선택하고, 


Build AssetBundle메뉴를 선택하겠습니다.




그러면 이렇게 에셋번들 만들기를 시작합니다.




이제 프로젝트 루트 폴더(Assets)를 가보면, 


우리가 만든 myAssetBundle이라는 파일이 생겼을 것입니다.


* 확장자는 없어도 되고, 마음대로 넣으셔도 됩니다.




이제 실제 에셋번들을 로드하도록 하겠습니다.


저는 Loader라는 클래스를 만들겠습니다.


< Loader.cs 코드 >


using UnityEngine;
using System.Collections;

public class Loader : MonoBehaviour {

	// Update is called once per frame
	void Update () {
        if (Input.GetKeyDown(KeyCode.A))
        {
            StartCoroutine(Load());
        }
	}

    IEnumerator Load()
    {
        // 불러올 파일 경로.
        // pc의 경우 file:/// 로 시작하셔야 합니다.
        string path = "file:///" + Application.dataPath + "/myAssetBundle";
        // 캐시에 있을 경우 그대로 로드하고, 그렇지않은 경우 해당 경로에서 다운로드하여 캐시저장.
        WWW ww = WWW.LoadFromCacheOrDownload(path, 0);
        // 받을 때까지 대기합니다.
        yield return ww;
        // 에러가 있을 수 있으므로, 체크합니다.
        if (string.IsNullOrEmpty(ww.error) == false)
        {
            Debug.LogError("Error!! : " + ww.error);
        }
        // 이제 에셋번들을 가져오겠습니다.        
        AssetBundle bundle = ww.assetBundle;
        // 우리가 만든 어셋번들에는 Cube, Cube1, Cube2가 있습니다.
        // 세개 다 로드하겠습니다.
        if (bundle != null)
        {
            // 우리는 폴더을 선택하여 에셋번들을 만들었으므로, 
            // Selection.activeObject가 없습니다.
            // 즉, bundle.mainAsset이 없습니다.
            // 하위에 있는 프리팹을 이름으로 로드해야 합니다.
            // bundle.mainAsset <= null.
            GameObject gObj = bundle.Load("Cube", typeof(GameObject)) as GameObject;

            if (gObj == null)
            {
                Debug.LogError("gObjA not found");            
            }

            Instantiate(gObj);

            gObj = bundle.Load("Cube1", typeof(GameObject)) as GameObject;

            if (gObj == null)
            {
                Debug.LogError("gObjB not found");
            }

            Instantiate(gObj);

            gObj = bundle.Load("Cube2", typeof(GameObject)) as GameObject;

            if (gObj == null)
            {
                Debug.LogError("gObjC not found");
            }

            Instantiate(gObj);
            // 모두 사용했다면, unload하겠습니다.
            // 실제 매니저를 만드실 때에는 실시간으로load, unload를 해도 되겠지만, 
            // 그럴필요는 없습니다.
            // 해당 에셋번들을 들고 있어도 무관합니다.(압축되어있는 상태이므로)
            // unload를 false로 하면 에셋번들 내에 활성화 되어있는 오브젝트는 
            // unload되지 않습니다.
            // 만약 true로 하면, 활성화 되어있어도 unload되어버립니다.
            // 이럴 경우 missing이 날 수 있습니다.
            bundle.Unload(false);
        }
        else
        {
            Debug.Log("null");
        }
    }
}


코드 작성이 완료되었으면, 게임오브젝트에 


스크립트를 추가해주세요.


그리고 실행을 시켜줍니다.


우리는 A키를 누르면 로드하도록 되어있습니다.


A를 눌러보면 다음처럼 로드되는것을 확인할 수 있습니다.


< 결과화면 >



유니티4 에셋번들 만들 때 보면 


BuildPipeline.PushAssetDependencies() 와 Pop이 있는데


다른 에셋번들 사이에서 의존성을 유지시킬 때 사용합니다.


참고 : BuildPipeline.PushAssetDependencies


궁금한 점은 댓글 달아주세요.




336x280(권장), 300x250(권장), 250x250, 200x200 크기의 광고 코드만 넣을 수 있습니다.



유니티 버전 : 5.3.4f


유니티3D 5.x (Unity3d 5) 어셋번들 매니저(AssetBundle Manager)


어셋번들(AssetBundle) 관련 사항들이 유니티 4.x와 5.x가 많이 달라졌습니다.


그래서 간단한 테스트로 정리해봅니다.


AssetBundle 빌드하는건 에셋스토어나 유니티 사이트에 있으니 미리 다운 바랍니다.


에셋스토어 링크 : https://www.assetstore.unity3d.com/kr/#!/content/45836



우선 저는 다음과 같은 구조로 만들었습니다.


DataForCDN이라는 폴더 내에 있는 데이터들을 어셋번들로 만들 계획입니다.


테스트를 위해 두 종류로 만들것입니다.


1. GoblinsB 폴더.


2. Beholders에 있는 Prefabs폴더에 있는 프리팹 한개.




그리고 Script에 있는 ReApplyShaders가 스크립트가 있는데 (NonCachingLoad는 신경쓰지마세요)


하는 역할은 머테리얼에 있는 쉐이더를 재적용 해주는 스크립트 입니다.


유니티5 로 어셋번들(assetbundle)을 묶어서 로드하게 되면


쉐이더가 적용 안되는 버그가 있습니다. 그래서 저렇게 (우선은..) 하고 있습니다.


저는 미리 로드할 프리팹(아래보이는 화면)에 저 스크립트 붙여놨습니다. 


< ReApplyShaders 코드 >


using UnityEngine;
using System.Collections;

public class ReApplyShaders : MonoBehaviour {

    Renderer[] renderers;
    Material[] materials;
    string[] shaders;

    void Awake()
    {
        renderers = GetComponentsInChildren();
    }

    void Start()
    {
        foreach (var rend in renderers)
        {
            materials = rend.sharedMaterials;
            shaders = new string[materials.Length];

            for (int i = 0; i < materials.Length; i++)
            {
                shaders[i] = materials[i].shader.name;
            }

            for (int i = 0; i < materials.Length; i++)
            {
                materials[i].shader = Shader.Find(shaders[i]);
            }
        }
    }
}




GoblinsB를 어셋번들로 만들기 위해 폴더 선택을 하고 다음처럼 이름을 써 줍니다.


확장자는 쓰지 않으셔도 되고 써도 됩니다.


그리고 저는 지금 대/소문자로 막 썼는데, 실제 대문자는 인식되지 않습니다.


(대문자로 써도 소문자로 바뀝니다. 보기 쉽게 저렇게 썼습니다)




두번째 AssetBundle로 BeholderGreen을 만들어 줍니다.




이제 마우스 오른쪽(또는 상단메뉴)를 통해 Build AssetBundles를 해줍니다.




그러면 다음처럼 진행이 됩니다.




Assetbundle Manager를 유니티에서 제공한 것을 사용했다면, 


프로젝트 폴더에 AssetBundles라고 폴더가 생기고 플렛폼이름 하위로


다음처럼 번들들이 생겼을 것입니다.


시간이 되신다면 manifest파일 한번씩 열어보시기 바랍니다.




실제 로드 테스트를 위해 위의 파일들을 다른 경로로(저는 E드라이브로) 옮겨줍니다.




이제 다음 코드를 붙여서 테스트를 해보도록 하겠습니다.


< 어셋번들 로드 하는 코드 >


using UnityEngine;
using System.Collections;

public class NonCachingLoad : MonoBehaviour {
    
    public string URL = "file:///E:/Android/";

	// Use this for initialization
	void Start () {
        
	}
	
	// Update is called once per frame
	void Update () {
	    if(Input.GetKeyDown(KeyCode.A))
        {
            StartCoroutine(Down());
        }
	}

    IEnumerator Down()
    {        
        WWW www = new WWW(URL + "Android");

        yield return www;

        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.Log(www.error);
            yield break;
        }

        // 메니페스트 얻기
        AssetBundle bundleManifest = www.assetBundle;
        www.Dispose();
        AssetBundleManifest assetBundleManifest = bundleManifest.LoadAsset("AssetBundleManifest");
        // 메니페스트가 가지고 있는 모든 어셋번들 이름을 가져옵니다.
        // 여기에서는 두개가 나오겠죠.
        string[] assetBundleNames = assetBundleManifest.GetAllAssetBundles();
        // 어셋번들 갯수만큼 돌면서 게임오브젝트를 만들겠습니다.
        for (int i = 0; i < assetBundleNames.Length; i++)
        {            
            string assetBundleName = assetBundleNames[i];
            // 이미 캐쉬되어있는지 확인
            bool bCaching = Caching.IsVersionCached(URL + assetBundleName, assetBundleManifest.GetAssetBundleHash(assetBundleName));
            // 로그 찍어본다.
            Debug.Log(assetBundleName + " Cash : " + bCaching.ToString());

            // 어셋번들을 다운로드 합니다.
            // 저는 URL과 해쉬값으로 로드하겠습니다.(해쉬값은 메니페스트에 있습니다.)
            www = WWW.LoadFromCacheOrDownload(URL + assetBundleName, assetBundleManifest.GetAssetBundleHash(assetBundleName));
            yield return www;
            AssetBundle bundle = www.assetBundle;

            string[] names = bundle.GetAllAssetNames();
            for (int j = 0; j < names.Length; j++)
            {
                // 하나의 어셋번들이 가지고 있는 리스트롤 로그에 찍을꺼에요.
                Debug.Log(names[j]);
                // GameObject인 것만 생성(여기에서는 프리팹만 나오겠죠)
                GameObject gObj = bundle.LoadAsset(names[j]);
                if (gObj != null)
                    Instantiate(gObj);
            }
           
            // 번들을 언로드해줍니다.
            bundle.Unload(false);
            www.Dispose();
        }      
    }
}


스크립트를 아무대나 링크시키고 실행을 해보면


다음처럼 로드가 됩니다. 사실 끝난거죠.




이제 버전관리를 위해(패치는 해야하니까) 고블린 중에 하나 이름을 바꾸겠습니다.




그리고 다시 AssetBundle build를 하고 아까 그 폴더로 복사/붙여넣기 합니다.


그리고 다시 플레이를 해보세요.


아래 로그 보이시나요?


고블린 어셋번들이 캐쉬에 저장이 안된 상태로 되어있습니다.


좀전에 이름을 바꿨으니깐요.



실제 버전관리, 암호화 등을 하려면 관리자를 만들어야 합니다.


현재 포스팅은 기본적인 개념을 위해 작성한 것이니 


여러 테스트를 해보시고 잘 만드시길 바랍니다.



참고 : http://dev.suriyun.com/bay/technical/unity-asset-bundle-part-2/



+ Recent posts