본문 바로가기

프로그래밍/Unity

[Unity] AssetBundle

에셋을 에셋번들에 할당


에셋 선택해 인스펙터에서 에셋 번들 생성하거나 기존 번들에 할당하면 해당 에셋은 에셋번들에 마킹된 것으로 처리된다.

에셋번들은 폴더같은 계층 구조로 생성이 가능하며,  에셋번들 할당시 "상위이름/하위이름" 형태로 마킹하면

계층구조로 만들어진다.


ex) mybundle/material


스크립트에서 에셋 번들로 할당

public class CreateAssetBundles

{

[MenuItem("Assets/Assign AssetBundle")]

static void AssignAsset()

{

// 선택된 에셋

Object[] assets = Selection.GetFiltered( typeof(Material), SelectionMode.DeepAssets);


// SelectionMode

// UnFiltered : 모든 선택항목 반환

// TopLevel : 최상위 transform 만 반환

// Deep : 선택된 항목과 자식 transform 항목들 반환

// Editable : edit 가능한 항목만 반환

// ExcludePrefab : 프리팹 제외

// Assets : Asset 폴더내의 오브젝트들만 반환

// DeepAssets : 폴더가 선택된 경우 하위의 모든 에셋들 반환


foreach( Object asset in assets)

{

string path = AssetDatabase.GetAssetPath( asset );

AssetImporter assetImporter = AssetImporter.GetAtPath( path );

// assetImporter.assetBundleName = "mybundle/material";

// assetImporter.assetBundleVariant = "";

assetImporter.SetAssetBundleNameAndVariant("mybundle/material", "");



}

}

}




빌드

전체 에셋 번들 빌드

public static AssetBundleManifest 

BuildAssetBundle ( 

string outputPath, 

BuildAssetBundleOptions assetBundleOptions, 

BuildTarget targetPlatform

);



Assets 폴더에 Editor 폴더 생성 후 스크립트 추가


using UnityEditor;


public class CreateAssetBundles

{

[MenuItem("Assets/Build AssetBundles")]

static void BuildAllAssetBundles()

{

string assetBundleDirectory = "Assets/AssetBundles";

if( !Directory.Exists(assetBundleDirectory))

{

Directory.CreateDirectory(assetBundleDirectory);

}


BuildPipeline.BuildAssetBundles

assetBundleDirectory, 

BuildAssetBundleOptions.None, 

BuildTarget.StandaloneWindow);

}

}




BuildAssetBundleOptions

.None

압축된 단일 LZMA 스트림인 LZMA 압축 포맷 사용

LZMA 압축포맷은 사용하기 전에 전체 번들의 압축을 풀어야 하므로, 로드 시간이 길어짐


.UncompressedAssetBundle

압축하지 않음


.ChunkBasedCompression

LZ4 압축 사용

에셋 사용시 모든 번들의 압축을 풀 필요 없이 청크 단위로 로드



BuildTarget

.StandaloneOSX

.StandaloneWindows

.iOS

.Android

.

.

.

.

.

EditorUserBuildSettings.activeBuildTarget : 현재 빌드 설정이 된 플랫폼으로 타겟 자동 설정



메뉴에서 위 스크립트를 실행하면 에셋번들 파일과 에셋번들.manifest 파일들이 생성된다.


AssetBundles

AssetBundles.manifest

mybundle

mybundle.manifest





특정 에셋 번들만 빌드


public static AssetBundleManifest

 BuildPipeline.BuildAssetBundles( 

string outputPath,

AssetBundleBuild[] builds,

BuildAssetBundleOptions assetBundleOptions,

BuildTarget targetPaltform

);


두번째 인자에 AssetBundleBuild 배열을 추가하면 해당 번들만 빌드하게 된다.


빌드할 에셋번들 수만큼 AssetBundleBuild 구조체 생성

AssetBundleBuild[] buildMap = new AssetBundleBuild[2];


에셋번들 설정

buildMap[0].assetBundleName = "1st-bundle-name";

buildMap[0].assetBundleVariant = "";


에셋 path 배열

string[] asset_full_path = Directory.GetFiles(Application.dataPath + "/bundle", "*.*", SearchOption.AllDirectories );

ArrayList list = new ArrayList();

for( int i = 0; i<asset_full_path.Length; i++ )

{

string file = asset_full_path[i];

string ext = Path.GetExtenstion(file);

if( ext.Equals(".meta") )

{

continue;

}


file = file.Replace(Application.dataPath, "Asset");

file = file.Replace("\\","/");


int index = filePath.LastIndexOf("/");

file= file.Substring(index);


list.Add(file);

}

string[] assets = (string[])list.ToArray(typeof(string));



buildMap[0].assetNames = assets;


.

.

.



BuildPipeline.BuildAssetBundles("output-path", buildMap, BuildAssetBundleOptions.None, BuildTarget.activeBuildTarget);




에셋번들로 마킹된 에셋들의 path

위의 경우 에셋번들에 포함될 에셋들을 System.IO 클래스들을 사용해 직접 설정했으나, 

AssetDatabase 함수 AssetDatabase.GetAssetPathsFromAssetBundle("bundle-name"); 

를 사용해 프로젝트 폴더에서 에셋번들 이름이 마킹된 에셋들의 path를 간단하게 가져올 수 있다.









에셋 번들 로딩

로컬파일은 접근권한 문제가 있으므로, Application.persistentDataPath , Application.streamingAssetsPath 

로 정의된 읽기,쓰기가 가능한 위치를 사용하도록 한다. 

https://docs.unity3d.com/Manual/StreamingAssets.html




Win) 

persistentDataPath : c:/Users/User/AppData/LocalLow/CompayName/ProductName

dataPath : product.exe/exe_Data

streamingAssetPath : "Application.dataPath + "/StreamingAssets"


Mac)

persistentDataPath : ~/Library/Application Support/CompanyName/ProductName

dataPath : product.app/Contents/Data

streamingAssetPath : Application.dataPath + "/StreamingAssets"


이전버전

persistentDataPath : ~/Library/ApplicationSupport/unity.CompanyName.ProductName



Android)

persistentDataPath : /data/data/com.Company.ProductName/files

persistentDataPath : /storage/sdcard0/Android/data/com.Company.ProductName/files


dataPath : /data/app/com.Company.ProductName.apk

streamingAssetPath : "jar:file://" + Application.dataPath + "!/assets"



iOS)

persistentDataPath : /var/mobile/Containers/Data/Application/RandomName/Documents

dataPath : /var/mobile/Containers/Data/Applications/RandomName/product.app/Data

streamingAssetPath :  Application.dataPath + "/Raw";




로컬파일

unity에서 접근가능한 파일 위치의 경우 간단하게 읽어올 수 있다.

AssetBundle bundle = AssetBundle.LoadFromFile( Path.Combine(Application.streamingAssetsPath, "bundle-name" ));



로컬 파일 async loading

AssetBundleRequest bundleReq = AssetBundle.LoadFromFileAsync( bundle-path + bundle-name );

yield return bundleReq;

AssetBundle bundle = bundleReq.assetBundle;



매니페스트 로드

종속적인 번들을 로드하는 경우 AssetBundleManifest 를 사용한다.


AssetBundle bundle = AssetBundle.LoadFromFile(  bundle-path + bundle-name  );

AssetBundleManifest manifest = bundle.LoadAsset<AssetBundleManifest>("AssetBundleManifest");



// 종속성이 있는 경우 종속된 번들 로드

string[] dependencies = manifest.GetAllDependencies( bundle-name );

foreach( string dependency in dependencies )

{

AssetBundle.LoadFromFile( Path.Combine( assetBundlePath, dependency ));

}






에셋 로딩


번들이 로드되었으면, 해당 번들에 포함된 실제 에셋들을 로드해야 한다.


// 단일 에셋로딩

T object = bundle.LoadAsset<T>( "asset-name" );



// 전체 에셋 로딩

Unity.Object[] objectArray = bundle.LoadAllAssets();



// 비동기 로딩

AssetBundleRequest assetReq = bundle.LoadAssetAsync<GameObject>("asset-name");

yield return assetReq;

var loadedAsset = assetReq.asset;




// 전체 에셋 비동기 로딩

AssetBundleRequest assetReq = bundle.LoadAllAssetsAsync();

yield return assetReq;

var loadedAssets = assetReq.allAssets;





url을 통한 에셋 로딩

어플리케이션에서 로컬로는 접근하지 못하는 path나 네트워크 환경의 번들의 경우 UnityWebRequest 를 통해 로드한다.



IEnumerator loadAsset()

{

string uri = "file:///Users/user/AssetBundles/" + assetBundleName;

UnityEngine.Networking.UnityWebRequest request = 

UnityEngine.Networking.UnityWebRequest.GetAssetBundle( uri, 0 );

yield return request.SendWebRequest();


AssetBundle bundle = DownloadHandlerAssetBundle.GetContent( request );

GameObject obj = bundle.LoadAsset<GameObject>("asset-name");

GameObject.Instantiate( obj );

}



Unity 2018.1 이상

UnityEngine.Networking.UnityWebRequest request = 

UnityEngine.Networking.UnityWebRequestAssetBundle.GetAssetBundle





씬 로딩

씬의 경우 path+ scene-name.unity  형태로 저장되어 있으므로, 일단 전체 경로를 얻고 이름 부분만 가져온다.

string[] scenes = bundle.GetAllScenePaths();

string firstScene = Path.GetFileNameWithoutExtention( scenes[0] );

SceneManager.LoadScene( firstScene );





번들 해제

bundle.Unload(false);

true 를 주면 번들에서 로드된 에셋까지 모두 해제하고, false일 경우 번들만 해제한다.

단, false로 해제한뒤 다시 번들을 로드하면 기존 에셋과 중복으로 에셋들이 로드되므로 번들 로드가 씬 초기에 한번만 발생하는 경우에 사용.





비동기 로딩

AssetBundleRequest는 AsyncOperation을 상속받는 클래스이다.


AsyncOperation

AssetBundleRequest

ResourceRequest



AsyncOperation 

기본 프로퍼티 정보

allowSceneActivation : 로드가 완료되면 씬을 활성화 시킬지 여부

isDone : 로드 완료 여부

progress : 0~1 진행률

priority 



이벤트

completed




아래는 씬을 비동기 로딩하는 샘플

리턴되는 AsyncOperation 값을 가져와 현재 진행 상황을 체크하게 된다.



// 진행 표시를 위한 delegate

public delegate void ProgressDelegate( float progress );


// 진행 표시를 위한 delegate의 이벤트

ProgressDelegate progressEvent = new ProgressDelegate( onPorgress );


void onProgress( float progress )

{

// 진행 상황 표시

}



// StartCoroutine을 위한 함수작성

IEnumerator LoadScene( string scene )

{

AsyncOperation op = SceneManager.LoadSceneAsync( scene );

op.allowSceneActivation = false;


while( ! op.isDone )

{

progressEvent( op.progress );


yield return null;

}


progressEvent( 1 );

yield return null;


op.allowSceneActivation = true;

}




'프로그래밍 > Unity' 카테고리의 다른 글

[Unity] Post Process Stack v2  (0) 2018.10.31
[Unity] AR Foundation Package  (0) 2018.09.20
[Unity] 유니티용 DLL  (0) 2018.09.18
[Unity] VR 환경 끄거나 켜기  (0) 2018.09.14
[Unity] AssetDatabase  (0) 2018.09.12
[unity3d] 서피스 쉐이더 정리  (1) 2018.06.26
[Unity] Mesh 생성 및 변경  (0) 2011.06.24
[Unity] 인스펙터에 UI 추가하기  (0) 2011.06.15
2D를 위한 Plane 생성  (0) 2011.05.30
Physics.Raycast  (0) 2011.05.13