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

카테고리

분류 전체보기 (2797)
Unity3D (853)
Programming (479)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
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

[수정] Rename 방식으로 변경(2024-07-04]

----

 

[빌드 시, Builtin AssetBundles에서 해당 Platform 에셋번들만 빌드 되도록 하기 위한 PreProcessor 추가]

기본적으로 에셋번들은 플랫폼 별로 모두 가지고 있는데 해당 플랫폼에 맞는 에셋번들만 빌드 되도록 하기 위해서

빌드 전(Preprocessor) 해당 안되는 AssetBundle을 Hidden 폴더(예:.Android or .iOS)로 Rename 했다가, 빌드 후(Postprocessor) 되돌려 놓음.

 

using System;
using UnityEngine;
using UnityEditor;
using UnityEditor.Build;
using System.IO;
#if UNITY_2018_1_OR_NEWER
using UnityEditor.Build.Reporting;
#endif
using UnityEditor.Callbacks;

namespace blueasa
{
    /// <summary>
    /// 빌드 시, Built-in AssetBundle에서 해당 Platform AssetBundle만 빌드 되도록 하기 위한 Preprocessor
    /// 빌드 전(Preprocessor) 해당 안되는 AssetBundle을 Hidden 폴더(예:.Android or .iOS)로 Rename 했다가, 빌드 후(Postprocessor) 되돌려 놓음
    /// [Hidden(./~) 참조] https://docs.unity3d.com/kr/2021.3/Manual/SpecialFolders.html
    /// </summary>
#if UNITY_2018_1_OR_NEWER
    public class BuildPreprocessor_BuiltinAssetBundle : IPreprocessBuildWithReport, IPostprocessBuildWithReport
#else
    public class BuildPreprocessor_BuiltinAssetBundle : IPreprocessBuild, IPostprocessBuild
#endif
    {
        private static readonly string m_strAndroid = "Android";
        private static readonly string m_strDotAndroid = ".Android";
        private static readonly string m_striOS = "iOS";
        private static readonly string m_strDotiOS = ".iOS";
        private static readonly string m_strDotMeta = ".meta";
        private static readonly string m_strAssetBundles = "AssetBundles";

        private static readonly string m_strDataPath = Application.dataPath;
        private static readonly string m_strStreamingAssetsPath = Application.streamingAssetsPath;
        
        private static readonly string m_strAssetBundlesPath_Builtin_FullPath = string.Format("{0}/{1}", m_strStreamingAssetsPath, m_strAssetBundles);
        private static readonly string m_strAssetBundlesPath_Builtin_Android_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_strAndroid);
        private static readonly string m_strAssetBundlesPath_Builtin_DotAndroid_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_strDotAndroid);
        private static readonly string m_strAssetBundlesPath_Builtin_iOS_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_striOS);
        private static readonly string m_strAssetBundlesPath_Builtin_DotiOS_FullPath = string.Format("{0}/{1}", m_strAssetBundlesPath_Builtin_FullPath, m_strDotiOS);

        public int callbackOrder { get { return 0; } }

        private static void Refresh()
        {
            System.Threading.Thread.Sleep(100);
            AssetDatabase.Refresh();
            System.Threading.Thread.Sleep(100);
        }

#if UNITY_2018_1_OR_NEWER
        public void OnPreprocessBuild(BuildReport report)
#else
        public void OnPreprocessBuild(BuildTarget target, string path)
#endif
        {
            Debug.LogWarning($"[OnPreprocessBuild] {this}");

            // 빌드 전, 다른 플랫폼 AssetBundle 폴더 임시 제외
#if UNITY_ANDROID
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_iOS_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_DotiOS_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename 'iOS' Folder to '.iOS'");
                Directory.Move(m_strAssetBundlesPath_Builtin_iOS_FullPath, m_strAssetBundlesPath_Builtin_DotiOS_FullPath);
                // 빈(Empty) 폴더 생성을 방지하기 위해서 폴더의 meta 파일도 함께 삭제
                if (true == File.Exists(m_strAssetBundlesPath_Builtin_iOS_FullPath + m_strDotMeta))
                {
                    File.Delete(m_strAssetBundlesPath_Builtin_iOS_FullPath + m_strDotMeta);
                }
                Refresh();
            }
#elif UNITY_IOS
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_Android_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_DotAndroid_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename 'Android' Folder to '.Android'");
                Directory.Move(m_strAssetBundlesPath_Builtin_Android_FullPath, m_strAssetBundlesPath_Builtin_DotAndroid_FullPath);
                // 빈(Empty) 폴더 생성을 방지하기 위해서 폴더의 meta 파일도 함께 삭제
                if (true == File.Exists(m_strAssetBundlesPath_Builtin_Android_FullPath + m_strDotMeta))
                {
                    File.Delete(m_strAssetBundlesPath_Builtin_Android_FullPath + m_strDotMeta);
                }
                Refresh();
            }
#endif
            // Start listening for errors when build starts
            Application.logMessageReceived += OnBuildError;
        }

        // CALLED DURING BUILD TO CHECK FOR ERRORS
        private void OnBuildError(string condition, string stacktrace, LogType type)
        {
            Debug.LogWarning($"[OnBuildError] {condition} {stacktrace} {type}");

            if (type == LogType.Error)
            {
                // FAILED TO BUILD, STOP LISTENING FOR ERRORS
                Application.logMessageReceived -= OnBuildError;

                // 빌드 에러 시에도 이동된 파일 되돌리기
                RestoreTemporarilyMovedAssetbundles();
            }
        }

#if UNITY_2018_1_OR_NEWER
        public void OnPostprocessBuild(BuildReport report)
#else
        public void OnPostprocessBuild(BuildTarget target, string path)
#endif
        {
            Debug.LogWarning($"[OnPostprocessBuild] {this}");

            // [빌드 후] 제외 됐던 다른 플랫폼 AssetBundle 폴더 되돌리기
            RestoreTemporarilyMovedAssetbundles();

            // IF BUILD FINISHED AND SUCCEEDED, STOP LOOKING FOR ERRORS
            Application.logMessageReceived -= OnBuildError;
        }

        public static void RestoreTemporarilyMovedAssetbundles()
        {
            Debug.LogWarning($"[RestoreTemporarilyMovedAssetbundles]");

            // [빌드 후] 제외 됐던 다른 플랫폼 AssetBundle 폴더 되돌리기
#if UNITY_ANDROID
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_DotiOS_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_iOS_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename '.iOS' Folder to 'iOS'");
                Directory.Move(m_strAssetBundlesPath_Builtin_DotiOS_FullPath, m_strAssetBundlesPath_Builtin_iOS_FullPath);
                Refresh();
            }
#elif UNITY_IOS
            // [Rename]
            if (true == Directory.Exists(m_strAssetBundlesPath_Builtin_DotAndroid_FullPath)
                && false == Directory.Exists(m_strAssetBundlesPath_Builtin_Android_FullPath))
            {
                Debug.LogWarning("[OnPreprocessBuild] Rename '.Android' Folder to 'Android'");
                Directory.Move(m_strAssetBundlesPath_Builtin_DotAndroid_FullPath, m_strAssetBundlesPath_Builtin_Android_FullPath);
                Refresh();
            }
#endif
        }
    }
}

 

 

반응형
Posted by blueasa
, |

[TroubleShooting]


[에러메시지]

NotSupportedException: .... System.Net.WebRequest.GetCreator (System.String prefix) System.Net.WebRequest.Create (System.Uri requestUri)


PC/iOS에서는 잘 되는데 Android에서 위와 같은 NotSupportedException이 난다.(현재 Unity v5.6.5f1)

검색해보니 게임코디에 아래와 같은 답변을 해주신 분이 있다.



  비회원: 
kimsama

NotSupportedException: .... System.Net.WebRequest.GetCreator (System.String prefix) System.Net.WebRequest.Create (System.Uri requestUri) 모바일에서 위의 에러로 인해 HttpWebReaquest 를 사용하지 못하는 경우라면 여기 링크의 내용 참고하시기 바랍니다. http://www.vovchik.org/blog/13001 간단하게 우회하는 방법이 나와 있습니다.

[출처] http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_QnA_etc&page=2&sn1=&divpage=1&sn=off&ss=on&sc=on&select_arrange=headnum&desc=asc&no=2617



그래서 링크(http://www.vovchik.org/blog/13001)에서 말해준 대로 Wrapping을 해서 Android에서 돌려보니 잘된다!!

(kimsama님 감사합니다!)


내가 추가 및 수정한 소스는 아래와 같다.


// Wrapper Class
using System;
using System.Net;

public class HttpRequestCreator : IWebRequestCreate 
{
	public WebRequest Create(Uri uri)
	{
		return new HttpWebRequest(uri);	
	}
}


    //Get size of the asset
    HttpRequestCreator cHttpRequestCreator = new HttpRequestCreator();            // modify
    System.Net.WebRequest req = cHttpRequestCreator.Create(new Uri(assetURL));    // modify
    req.Method = "HEAD";

    float ContentLength;
    using (System.Net.WebResponse resp = req.GetResponse())
    {
        float.TryParse(resp.ContentLength.ToString(), out ContentLength);
    }

    while (!download.isDone)
    {
        if (progressBar != null)
	    {
            progressBar.LabelInformations = "Downloading Assets";
            progressBar.Progress = download.progress;
            progressBar.AssetSize = ContentLength / 1000000; //(Mb)
	    }
	    yield return null;
    }






- CDN에 있는 파일 사이즈를 어떻게 알 수 없나 하고 찾다가 테스트 해보고 잘 되길래 올려 놓음.

  (PC/iOS는 잘되는데 Android가 에러나서 위와 같이 고침)

    1. //Get size of the asset
    2. System.Net.WebRequest req = System.Net.HttpWebRequest.Create(assetURL);
    3. req.Method = "HEAD";
    4. float ContentLength;
    5. using (System.Net.WebResponse resp = req.GetResponse())
    6. {
    7. float.TryParse(resp.ContentLength.ToString(), out ContentLength);
    8. }
    9. while (!download.isDone)
    10. {
    11. if (progressBar != null)
    12. {
    13. progressBar.LabelInformations = "Downloading Assets";
    14. progressBar.Progress = download.progress;
    15. progressBar.AssetSize = ContentLength / 1000000; //(Mb)
    16. }
    17. yield return null;
    18. }


[출처] https://answers.unity.com/questions/1035361/get-size-of-an-online-assetbundle-and-progress-in.html




반응형
Posted by blueasa
, |
반응형
Posted by blueasa
, |
반응형
Posted by blueasa
, |



[링크] https://bitbucket.org/Unity-Technologies/assetbundledemo


[링크] https://assetstore.unity.com/packages/tools/utilities/assetbundle-manager-example-scenes-45836

반응형
Posted by blueasa
, |

5.2 이후 바뀐 에셋번들을 처음 접해봤는데 이전보다는 심플해져서 사용하긴 쉬운 것 같다.


근데 서버에서 외부(앱 내 저장 가능한 공간 : Application.persistentDataPath 사용)에 다운로드를 받고 로드 할 때 삽질한 내용을 간단하게 올려 놓는다.


지금은 바빠서..


간단하게 올리고 나중에 퇴고를 해야겠다..



[삽질_1]

- PC에서는 잘되는데 Android에서 Application.persistentDataPath로만 접근 하는데,

  Write 할 때 Path가 mnt로 시작하는 외부 저장소 Path로 저장 됨.

  AssetBundle을 로드 하기 위해 Read 하려고 접근하니 Stroage로 시작하는 내부 저장소 Path를 줌.

[처리_1]

- 우선 강제로 내부 저장소만 사용하도록 프로젝트 셋팅에서 변경

   (Project Settings-Android-Configuration-Write Permission 을 Internal로 변경)



[삽질_2]

- 빈번하게 에셋번들 로컬 로드 시, 무한루프에 빠짐

[처리_2]

- 처리하기 편하려고 BundleName과 AssetName을 같게 했는데 유니티가 헷갈려하면서 뭔가 문제가 생기는 것 같아서

  BundleName 셋팅에 확장자를 추가(인스펙터에서 셋팅)하니 잘 됨.



[삽질_3]

- 제대로 다하고 올린 것 같은데 이전에 잘 로드 되던 파일이 에러가 나서 당황함.


[처리_3]

- 파일질라로 에셋번들 파일을 업로드 할 때 Binary로 올리지 않으면 문제가 생긴다고 함.

  파일질라 셋팅을 수정하고 다시 업로드 해서 해결

  (전송-전송 유형-바이너리 로 변경)

- [참조] http://blueasa.tistory.com/2110

  


반응형
Posted by blueasa
, |


[링크] http://unitylist.com/r/6js/unity-5-assert-bundle

반응형
Posted by blueasa
, |


[링크] https://github.com/kimsama/Unity5-AssetBundleSetting

반응형

'Unity3D > AssetBundle' 카테고리의 다른 글

[펌] 에셋번들 생성하기 & 다운로드  (0) 2017.04.12
[링크] 에셋번들 로딩 방식 4가지  (0) 2017.03.03
WWW.LoadFromCacheOrDownload  (0) 2015.04.23
AssetBundle 가이드  (0) 2015.01.26
AssetBundle 버전체크 방식..  (0) 2014.04.21
Posted by blueasa
, |

1. AssetBundle 란? 및 필요성

 - 어플리케이션의 Runtime 시 외부에서 Resource(Asset)를 가져올 수 있도록 해주는 Unity3D 고유의 기능

 - 패키지지향 게임과 서비스지향 게임(업데이트)

 - Web 기반과 Smart phone 기반에서의 분할 다운로드
   * 아이폰 앱스토어나 안드로이드 마켓에서 파일이 일정 크기 이상이면 3g로 다운로드가 허용되지 않으며 wi-fi 로 다운 받게 끔 되어 있음.
     여기서 wi-fi 모드로 다운 받게 끔 제한시 다운로드 횟수가 현저히 저하되는 경우 발생 (컨텐츠의 접근성에 심각한 문제가 발생)
     -> 초기 실행 파일을 작게하고 게임 내에서 다운로드 받는 기법 사용하여 해결, 이 경우 AssetBundle 을 사용하여 해결 가능

 

2. Native 구현 vs AssetBundle
  >>  AssetBundle는 Unity3D(Pro만!)에서 제공하는 런타임에 리소스를 가지고 오는 기술을 말하는데

        C#  등으로 직접 구현하는 것과 무슨 차이점이 있고 또, 어떤 특징이 있는가?

 - 모든 플랫폼에서 일관적인 방법으로 이용 가능 (Flash 빌드 제외!)
   * 웹플랫폼으로 만들경우 Sandbox 보안 정책이 적용 - 로컬에서 파일을 읽기는 가능하지만 저장하지 못함.
   * 다른 플랫폼에서는 가능 (stand-alone, ios, android) 
   * AssetBundle을 사용하면 웹플랫폼 빌드 일 때 로컬에 cache로 저장하는 방법이 사용 가능함

     (웹플레이어가 AssetBundle의 cache를 생성 해줌)

참고)  Flash 일경우에는 AssetBundle 및 WWW 클래스 사용 불가능, 웹서버를 사용하는 등의 우회적인 방법만 가능한 것으로 보임

 

 - Unity3D Editor에서 제공하는 Asset pipeline을 Runtime에 이용할 수 있는 유일한 수단
   * AssetBundle을 써야하는 가장 중요한 이유,  
   * 유니티는 에디터하고 플레이어하고 분리가 되어 있는 데 어셋을 불러와서 실제로 사용할 수 있게 가공해주는 건 에디터 밖에 못해줌
      >> 에디터에는 어셋을 임포트 해 주는 기능 하는 임포터가 존재함.
      >> 실제 유니티 프로젝트에서 보는 에셋들은 실제 데이타가 아니라 한번 가공한 데이타 (임포터 할때 마다 유니티 폴더 안 Library 내에 가공한 데이타가 들어감)
      >> 런타임 중에는 메쉬(fbx 파일) 등을 임포트 할 수 없다. 임포트를 하려면 임포터를 거쳐야하는데 임포터는 에디터 밖에 존재 하지 않음.
      >> 유니티는 네이티브한 데이타를 사용하는 데 상당히 제한적인 면이 있다. (예외적으로 텍스처(제약많음), 텍스트에셋 등은 임포터를 거치지 않고 사용할 수 있지만 
           그 외 기타 매터리얼, 게임 오브젝트, 메쉬 등의 오브젝트들은 임포터를 거쳐야 한다.)
      >> 어셋 번들에 에디터 내 임포터를 통해 가공된 데이타를 저장하여 런타임시 해당 어셋 번들을 사용할 수 있다.

 - Push, Pop의 개념을 이용한 AssetBundle끼리의 의존관계 설정 가능
   * 두개 모델이 같이 사용하는 텍스처를 모델과 따로 어셋번들로 뽑아서 두개 모델이 해당 텍스처를 같이 사용하는 식으로 사용 가능.

 - Memory관리 용도로 사용 가능

 - Caching과 Version관리기능 제공
   * 로컬에 캐싱해서 매번 다운로드를 방지. 또한 캐싱시 버전을 부여하여 관리 가능 (이것을 이용하면 온라인 게임의 Patch시스템 구현 가능)

 - WWW class를 이용한 비동기 다운로드가 가능

참고) 유니티 코리아 사이트 공식 문서 AssetBundle -> http://unitykoreawiki.com/index.php?n=KrMain.AssetBundles

3. AssetBundle의 이용 방법

  - 로드

    * WWW.WWW()   http://unity3d.com/support/documentation/ScriptReference/WWW.WWW.html -  가장 일반적인 사용방법
    * AssetBundle.CreateFromMemory()
       >> 메모리 부터 어셋 번들을 생성, binary data 를 읽어와서 메모리를 올려놓고 어셋 번들을 생성하는 기능. 암호화 등의 특수한 처리가 필요할 때 유용
       
    * WWW.LoadFromCacheOrDownload()


 - Asset 얻기

    * AssetBundle.Contains() : 특정 어셋이 있는지 확인
    * AssetBundle.Load() : 실제 어셋을 가져오는 것
    * AssetBundle.LoadAsync() : 어셋을 비동기로 가지고 옴


 - 메모리에서 제거

    * AssetBundle.Unload(bool unloadAllLoadedObjects)
      >> true : 강제로 모두 내려버림. 어셋간 링크가 깨짐. false : 사용하지 않는 것만 추려서 메모리에서 내림 ( GC.Collect()를 먼저 호출해주면 더 안정적으로 동작 )

 

4.  AssetBundle의 생성 방법
    >> BuildPipeline를 이용하여 에디터 상에서만 생성 가능    

 - BuildPipeline.BuildAssetBundle() : 어셋을 어셋번들로 만들 때 일반적으로 사용 

Scene 를 어셋 번들로 생성할 수 있는 방법 
 - BuildPipeline.BuildStreamedSceneAssetBundle() 

 - BuildPipeline.BuildPlayer()

참고)  BuildPipeline.BuildStreamedSceneAssetBundle() 와 BuildPipeline.BuildPlayer() 차이점 이란 ??


 

참고할 만한 예제 소개

 - AssetBundle

 - Caharacter customization

 - Bundle loader

 - Bundle Sync


5. Caching을 이용한 버전과 메모리 관리

 - WWW.LoadFromCacheOrDownload()

 - Cache의 저장 경로

  * Windows7 : C:\Users\사용자이름\AppData\LocalLow\Unity\WebPlayer\Cache

  * Mac : 미확인

  * iOS : Document

  * Android : 미확인

 - Caching class를 이용하여 cache를 관리 Non Cache와 Cache 사용할 때의 메모리 사용방식의 차이가 생김 (Resource 클래스를 사용하는 경우와 유사)

 - Cache는 최대 4GB 까지 사용 가능. Caching class를 사용하여 조절
 - 웹빌드 일 경우 캐슁 라이센스에 영향을 받음 (다른 플렛폼에서는 별도의 캐슁 라이센스 없음)

 - WebPlayer/Cache/Shared 폴더의 사용 용량이 50MB로 제한되며 제한을 해제하기 위해서는 추가적인 라이센스 구매를 해야 함 (경험상 50MB제한이 제대로 동작 안하는 듯 함)

   ( 참고 : http://www.unity3dkorea.com/board/index.php?db=knowhow&no=118&mari_mode=view@view )

 

6. AssetBundle과 MonoScript의 관계

 - Unity3D에서 사용하는 C# 및JavaScript는 다른 일반적인 script언어처럼 인터프리터가 아닌 컴파일 언어이다. (컴파일 언어는 Runtime에 코드를 추가하기가 어려움)

 - 일반적인 경우

 - Assembly의 저장 (DLL?)

 - Lua script의 활용한다면?

 

7. 주의사항

 - AssetBundle.Load() 을 사용할 때 딸꾹질(랙)이 발생함

 - 용량이 큰 다수의 AssetBundle을 로드할 경우 Crash 발생율 큼

 - Web 빌드에서는 Caching.CleanCache() 가 작동 안함

 - Static Mesh를 사용하는 Scene을 Export하면 용량이 크게 증가함

 - Platform에 따라 별도로 제작되야 함

 - Unity3D의 버전에 지나치게 의존적

 - 내용물에 대한 보안기능을 제공 안함

 - 기본적으로 MonoScript의 컴파일 코드는 저장 못함


8. Effective AssetBundle

 - 다운로드를 Queue형태로 관리

 - Cache를 사용해서 Memory사용량을 관리

 - AssetBundle의 구성내용을 저장 및 관리

 - Platform 별로 자동 생성 기능의 필요

 - 보안을 위한 별도의 처리


출처 : https://www.assembla.com/spaces/a-h/wiki/AssetBundle_%EA%B0%80%EC%9D%B4%EB%93%9C/history

반응형

'Unity3D > AssetBundle' 카테고리의 다른 글

[링크] Unity5-AssetBundleSetting  (0) 2016.07.25
WWW.LoadFromCacheOrDownload  (0) 2015.04.23
AssetBundle 버전체크 방식..  (0) 2014.04.21
Unity AssetBundle Dependencies  (0) 2014.03.11
AssetBundle 생성부터 패치 및 적용까지.  (0) 2014.02.27
Posted by blueasa
, |

static bool BuildAssetBundle(Object mainAsset, Object[] assets, string pathName, uint crcBuildAssetBundleOptionsassetBundleOptions = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets,BuildTarget targetPlatform = BuildTarget.WebPlayer);

 이 함수를 사용해서 애셋번들을 생성하면 crc값을 얻을 수 있는데요 애셋번들에 포함되어있는 애셋들의 변경 사항이 있는 경우에 저 값이 변하더라고요. 이전에 만든 번들과 crc값이 다르면 수정된거라고 보고 패치를 진행하는 방식으로 구현했습니다.



반응형
Posted by blueasa
, |