에셋을 에셋번들에 할당
에셋 선택해 인스펙터에서 에셋 번들 생성하거나 기존 번들에 할당하면 해당 에셋은 에셋번들에 마킹된 것으로 처리된다.
에셋번들은 폴더같은 계층 구조로 생성이 가능하며, 에셋번들 할당시 "상위이름/하위이름" 형태로 마킹하면
계층구조로 만들어진다.
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 |