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

카테고리

분류 전체보기 (2797)
Unity3D (853)
Script (91)
Extensions (16)
Effect (3)
NGUI (81)
UGUI (9)
Physics (2)
Shader (37)
Math (1)
Design Pattern (2)
Xml (1)
Tips (201)
Link (23)
World (1)
AssetBundle (25)
Mecanim (2)
Plugins (79)
Trouble Shooting (70)
Encrypt (7)
LightMap (4)
Shadow (4)
Editor (12)
Crash Report (3)
Utility (9)
UnityVS (2)
Facebook SDK (2)
iTween (3)
Font (13)
Ad (14)
Photon (2)
IAP (1)
Google (8)
Android (51)
iOS (44)
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


[MenuItem("AssetBundles/Set Asset Bundle From File Name",false, 0)]
static void SetAssetBundlesFromFileNames()
{
	if (Selection.assetGUIDs.Length > 0) {
		foreach (ObjectassetinSelection.objects) {
			string path = AssetDatabase.GetAssetPath(asset);
			AssetImporter assetImporter = AssetImporter.GetAtPath(path);
			assetImporter.assetBundleName = asset.name;
			Debug.Log(Selection.assetGUIDs.Length + " Asset Bundles Assigned");
		}
	} else {
		Debug.Log ("No Assets Selected");
	}
}



[출처] https://forum.unity.com/threads/set-asset-bundle-name-form-editor-menu-item-script.333297/

반응형
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
, |

UnityWebRequest를 이용해서 원격 서버에서 받아온 에셋 번들을 로컬 저장소에 저장하는 방법

유니티 5.6 문서에서는 알려주지 않는 로컬 저장소 저장 방법

유니티 5.6 문서에서는 웹 서버에서 에셋 번들을 받아올 때 WWW.LoadFromCacheOrDownload 대신에 UnityWebRequest와 DownloadHandlerAssetBundle을 사용할 것을 권장하고 있다. 아래의 코드가 유니티 5.6 문서에서 보여주는 웹 서버에서 에셋 번들을 받아와서 메모리에 로드하는 예제이다.

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

public class UnityWebRequestExample : MonoBehaviour
{
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();

AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");

Instantiate(cube);
Instantiate(sprite);
}
}


출처: http://wergia.tistory.com/32 [베르의 프로그래밍 노트]
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class UnityWebRequestExample : MonoBehaviour
{
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();

AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");

Instantiate(cube);
Instantiate(sprite);
}
}


출처: http://wergia.tistory.com/32 [베르의 프로그래밍 노트]
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class UnityWebRequestExample : MonoBehaviour
{
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();

AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");

Instantiate(cube);
Instantiate(sprite);
}
}


출처: http://wergia.tistory.com/32 [베르의 프로그래밍 노트]
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class UnityWebRequestExample : MonoBehaviour
{
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();

AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");

Instantiate(cube);
Instantiate(sprite);
}
}


출처: http://wergia.tistory.com/32 [베르의 프로그래밍 노트]
using UnityEngine;
using UnityEngine.Networking;

public class UnityWebRequestExample : MonoBehaviour
{
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();

AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");

Instantiate(cube);
Instantiate(sprite);
}
}
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class UnityWebRequestExample : MonoBehaviour
{
IEnumerator InstantiateObject()
{
string uri = "file:///" + Application.dataPath + "/AssetBundles/" + assetBundleName;
UnityEngine.Networking.UnityWebRequest request = UnityEngine.Networking.UnityWebRequest.GetAssetBundle(uri, 0);
yield return request.Send();

AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(request);

GameObject cube = bundle.LoadAsset<GameObject>("Cube");
GameObject sprite = bundle.LoadAsset<GameObject>("Sprite");

Instantiate(cube);
Instantiate(sprite);
}
}


출처: http://wergia.tistory.com/32 [베르의 프로그래밍 노트]

서버에서 받아온 에셋 번들을 메모리에 넣어두고 사용하고자 하는 목적이 아니라 받아온 에셋 번들을 클라이언트의 로컬 저장소에 저장하고 패치하는 시스템을 만들고자할 때에는 부적절한 예제이며, 로컬 저장소에 저장하는 방법은 유니티 5.6의 문서에서는 알려주지 않는다.



에셋 번들을 로컬 저장소에 저장하는 방법

서버에서 받아온 에셋 번들을 로컬 저장소에 저장하려면 받아온 데이터를 파일 입출력을 해야했는데, 그러기 위해서는 우선 받아온 에셋 번들의 데이터, byte[] data를 찾아내고 거기에 엑세스할 수 있어야 했다. 그래서 첫 번째로 시도한 방법이 위의 예제에서 UnitWebRequest.GetAssetBundle()로 받아온  UnityWebRequest request에서 request.downloadHandler.data를 통해서 접근하는 것이었다.

using System.IO;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class AssetLoader : MonoBehaviour
{
public string[] assetBundleNames;

IEnumerator AccessFailMethod()
{

string uri = "http://127.0.0.1/character";


UnityWebRequest request = UnityWebRequest.GetAssetBundle(uri);

yield return request.Send();
string assetBundleDirectory = "Assets/AssetBundles";

if (!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}

FileStream fs = new FileStream(assetBundleDirectory + "/" + "character.unity3d", System.IO.FileMode.Create);
fs.Write(request.downloadHandler.data, 0, (int)request.downloadedBytes);
fs.Close();
}
}

하지만 위의 방법은 에셋 번들에 대한 원시 데이터 접근은 지원하지 않는다는 예외를 발생시키고 실패한다. 즉, GetAssetBundle로 웹 서버에서 불러온 데이터는 데이터에 대한 직접 접근을 허용하지 않으니 파일 입출력을 통해서 로컬 저장소에 저장할 수 없다는 것이다.


그렇기 때문에 로컬 저장소에 저장하기 위해서는 웹 서버에서 에셋 번들을 받아올 때 다른 방법을 취해야 했다. 아래의 예제가 다른 방식으로 웹 서버에서 에셋 번들을 받아와서 로컬 저장소에 저장하는 예제이다 :

using System.IO;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class AssetLoader : MonoBehaviour
{

// 서버에서 받아오고자 하는 에셋 번들의 이름 목록

// 지금은 간단한 배열 형태를 사용하고 있지만 이후에는

// xml이나 json을 사용하여 현재 가지고 있는 에셋 번들의 버전을 함께 넣어주고

// 서버의 에셋 번들 버전 정보를 비교해서 받아오는 것이 좋다.
public string[] assetBundleNames;

IEnumerator SaveAssetBundleOnDisk()
{

// 에셋 번들을 받아오고자하는 서버의 주소

// 지금은 주소와 에셋 번들 이름을 함께 묶어 두었지만

// 주소 + 에셋 번들 이름 형태를 띄는 것이 좋다.
string uri = "http://127.0.0.1/character";


// 웹 서버에 요청을 생성한다.
UnityWebRequest request = UnityWebRequest.Get(uri);
yield return request.Send();


// 에셋 번들을 저장할 경로

string assetBundleDirectory = "Assets/AssetBundles";
// 에셋 번들을 저장할 경로의 폴더가 존재하지 않는다면 생성시킨다.
if (!Directory.Exists(assetBundleDirectory))
{
Directory.CreateDirectory(assetBundleDirectory);
}


// 파일 입출력을 통해 받아온 에셋을 저장하는 과정
FileStream fs = new FileStream(assetBundleDirectory + "/" + "character.unity3d", System.IO.FileMode.Create);
fs.Write(request.downloadHandler.data, 0, (int)request.downloadedBytes);
fs.Close();
}
}

위 예제에서 보다시피 UnityWebRequest.Get() API를 사용했을 경우 받아온 에셋 번들의 원시 데이터에 대한 접근이 가능해진다. 이로 인해서 파일 입출력을 통한 원격 서버에서 받아온 에셋 번들의 로컬 저장소 저장에 성공하게 되었다.



파일 입출력을 통한 저장 방법

받아온 에셋 번들을 파일 입출력을 통해 로컬 저장소에 저장하는 방법은 여러가지가 있다. 적당하다고 생각되는 방법을 골라서 사용하면 될 것이다.

// 파일 저장 방법 1
FileStream fs = new FileStream(assetBundleDirectory + "/" + "character.unity3d", System.IO.FileMode.Create);
fs.Write(request.downloadHandler.data, 0, (int)request.downloadedBytes);
fs.Close();

// 파일 저장 방법 2
File.WriteAllBytes(assetBundleDirectory + "/" + "character.unity3d", request.downloadHandler.data);
 

// 파일 저장 방법 3
for (ulong i = 0; i < request.downloadedBytes; i++)
{
fs.WriteByte(request.downloadHandler.data[i]);
// 저장 진척도 표시

}



로컬 저장소에 저장한 에셋 번들을 불러와서 사용하는 방법

위의 과정을 통해 원격 서버에서 받아온 에셋 번들을 로컬 저장소에 저장하는데 성공했다. 이 다음에 해야할 작업은 저장한 에셋 번들을 불러와서 사용하는 것이다. 그 작업은 유니티 5.6 문서에 나오는 기본 예제를 구현하는 것으로 충분히 가능하다.

using System.IO;
using System.Collections;
using UnityEngine;
using UnityEngine.Networking;

public class AssetLoader : MonoBehaviour
{
IEnumerator LoadAssetFromLocalDisk()
{
string assetBundleDirectory = "Assets/AssetBundles";
// 저장한 에셋 번들로부터 에셋 불러오기
var myLoadedAssetBundle = AssetBundle.LoadFromFile(Path.Combine(assetBundleDirectory + "/", "character.unity3d"));
if (myLoadedAssetBundle == null)
{
Debug.Log("Failed to load AssetBundle!");
yield break;
}
else
Debug.Log("Successed to load AssetBundle!");

var prefab = myLoadedAssetBundle.LoadAsset<GameObject>("P_C0001");
Instantiate(prefab, Vector3.zero, Quaternion.identity);
}
}



출처: http://wergia.tistory.com/36 [베르의 프로그래밍 노트]

반응형
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
, |

Answer by Diet-Chugg 

If you are using FIlezilla to upload your asset bundles. Try inside of FIlezilla Transfer>Transfer Type>BInary. then delete your build and asset bundles and re-upload them. It worked for me.

[한글] 전송-전송 유형-바이너리 로 변경



[출처] http://answers.unity3d.com/questions/1140569/error-failed-to-decompress-data-for-the-assetbundl.html

반응형
Posted by blueasa
, |

1. 애셋번들(AssetBundle)은 무엇인가요?

유니티에서 애셋(Asset)들을 묶어서 하나로 관리하는 파일을 의미합니다다른 사용자에게 다량의 파일을 전송할 때 디렉토리 구조 및 파일들을 모아 하나의 zip파일에 압축하여 전송하듯이게임 제작에 사용하는 애셋에 관련된 원본 데이터 및 관련 설정을 모두 포함하여 하나의 파일로 묶어 줍니다.


2. 그렇다면 애셋은 뭔가요?

애셋은 유니티 프로젝트에서 공용으로 사용하는 데이터를 의미합니다. 3D 모델링이미지사운드텍스트 및 스크립트 파일등 모든 데이터가 애셋이 될 수 있습니다더 쉽게 설명하면 프로젝트 뷰에 목록이 보이면 이 아이템들이 각각 애셋이라도 생각하셔도 무방합니다애셋은 파일 뿐만 아니라 메타 데이터라고 하는 엔진 내에서 데이터 활용을 위해 변환하는 정보도 같이 포함합니다그리고 에디터는 각 애셋에 고유한 GUID를 부여하고 관리합니다.

(참고로 에디터가 자동으로 GUID를 애셋에 부여하기 때문에, 다른 프로젝트의 애셋이 같은 GUID를 생성할 가능성이 있습니다. 이의 충돌을 해결하기 위해 서버쪽에서 통합적으로 GUID를 관리하는 솔루션이 애셋 서버입니다. )


3. 애셋번들은 왜 필요한가요?

유니티에서 게임을 만들고 빌드하면 실행파일과 함께 이를 뒷받침하는 애셋들이 제공됩니다이 애셋들은 유니티만의 고유한 포맷으로 압축 및 보안이 적용된 형태로 저장되어 있어서빌드 후에는 이를 변경하는 것이 불가능합니다애셋번들을 사용하게 되면게임에 사용되는 리소스들을 별도로 분리하여 나중에 스트리밍 방식으로 다운로드 받을 수 있도록 관리가 가능해집니다에셋번들을 통해 컨텐츠 패치를 수행할 수 있으며사이즈에 민감한 모바일의 경우초기 버젼은 사이즈를 작게 만들어서 배포하고 추가적으로 필요한 데이터들은 인터넷을 통해 점진적으로 받으면서 서비스할 수 있습니다.


4. 애셋번들은 어떻게 만드나요?

애셋번들은 유니티 에디터 라이브러리의 BuildPipeline 클래스를 통해 제작할 수 있습니다유니티 에디터에서 에셋번들을 만드는 메뉴는 없으며에디터를 확장해서 직접 제작해야 합니다일반적으로 아래 샘플 코드와 같이 에디터의 프로젝트 뷰에서 현재 선택된 파일 정보를 나타내는 Selection 클래스와 조합해서 사용합니다.

애셋 번들은 만드는 타입에 따라 크게 아래의 3가지로 구분할 수 있습니다.

일반 애셋 번들 모든 타입의 애셋을 묶어서 제작할 수 있습니다. BuildPipeline.BuildAssetBundle 함수를 사용합니다주 애셋을 지정하는 첫 번째 인자에 특정 애셋을 지정하면, AssetBundle.mainAsset 프로퍼티를 사용하여 바로 가져올 수 있습니다.

스트리밍 씬 애셋 번들 씬에 관련된 애셋을 묶어서 저장하며이를 사용하면 새로운 씬을 스트리밍으로 로딩할 수 있습니다. BuildPipeline.BuildStreamedSceneAssetBundle 함수를 사용합니다.

이름이 지정된 애셋 번들 일반 애셋번들과 동일하지만 각각 애셋에 사용자가 지정한 이름을 지정할 수 있습니다. BuildPipeline.BuildAssetBundleExplicitAssetNames 함수를 사용하며 BuildAssetBundle 함수와의 차이는 주 애셋(Main Asset)을 지정하는 기능이 없으며대신 사용자가 지정한 이름의 배열을 지정할 수 있습니다.

애셋 번들은 제작 옵션을 여러가지로 설정할 수 있는데설정할 수 있는 옵션들은 다음과 같습니다.

CollectDependencies : 특정 애셋에 연관된 다른 애셋을 모두 포함시킵니다.

CompleteAssets : 애셋이 속하는 게임 오브젝트와 관련된 모든 애셋을 포함시킵니다.

DisableWriteTypeTree : 유니티 버젼 정보이 달라도 애셋의 정보를 올바르게 인식할 수 있는 타입트리(TypeTree) 정보를 제거하여 애셋번들을 제작합니다.

DeterministicAssetBundle : 애셋번들을 다시 빌드하더라도 처음 애셋번들이 가지고 있던 GUID의 해시 값을 그대로 유지합니다.

UncompressedAssetBundle : 애셋번들을 제작할 때 압축하지 않습니다.

해당 옵션들은 모두 OR 연산에 의해서 복수개 지정이 가능합니다.

마지막으로 플랫폼에 따라 애셋번들간의 호환성이 다르기 때문에 반드시 빌드타겟을 명시해 주어야 합니다.

 

5. 제작한 애셋번들은 서로 호환되나요?

플랫폼 및 유니티 버젼에 따라 호환성 문제가 일어날 수 있습니다.

스탠드얼론 플랫폼 및 웹 플랫폼으로 제작된 애셋번들들은 서로 호환되지만아이폰 및 안드로이드 플랫폼 애셋번들은 다른 플랫폼의 애셋번들과 서로 호환되지 않습니다.

아래는 플랫폼에 따른 호환성을 정리한 표입니다예를 들어 웹플레이어아이폰안드로이드에 모두 호환되는 서비스를 제작할 계획을 가지고 있다면총 3가지 종류의 애셋번들을 제작해야 합니다.

그리고 유니티의 버젼에 따라 애셋번들의 호환성도 고려하여야 합니다.

유니티에는 타입트리(Type Tree)라는 데이터가 있는데이 데이터가 애셋 번들에 포함되어 있으면 다른 버젼에서 만든 애셋번들을 파악할 수가 있어서 애셋번들을 재활용할 수 있게 됩니다하지만 모든 플랫폼 애셋번들에 타입트리 정보가 포함되는 것은 아닙니다모바일용 애셋 번들은 처리속도를 위해 타입 트리 정보가 포함되지 않게 설계되어 있어 유니티 버젼이 변경되는 경우 애셋 번들을 다시 빌드해야 합니다스탠드얼론 플랫폼은 빌드시 사용자 설정에 따라 타입 트리 정보의 포함 유무를 지정할 수 있으며웹 플랫폼은 언제나 타입 트리 정보를 포함합니다.

 

6. 애셋 번들 이외에 패치를 받을 수 있는 방법은 없을까요?

애셋을 만들어 압축하고 암호화 거는 로직은 유니티만 알고 있기 때문에 공식적으로 애셋 번들이외에 실시간으로 데이터를 패치할 수 있는 방법은 없습니다웹의 경우 사용자 스크립트에서 파일을 생성하는 것을 제한하기 때문에캐싱 기능을 사용하려면 유니티 웹플레이어가 제공하는 애셋 번들 사용이 필수입니다.

 

7. 캐싱 기능은 무엇인가요?

이미 만들어진 게임에 인터넷을 통해 신규 컨텐츠를 제공하는 DLC(Downloadable Contents)기능을 구현하거나, MMORPG와 같이 너무 서비스 규모가 크고컨텐츠의 업데이트가 자주 발생하는 경우기본 실행 파일과 애셋 번들을 조합해서 사용하는데이 때 실행할 때 마다 매번 네트웍으로 새로운 컨텐츠를 다운 받게 되면 네트웍의 부하 및 속도의 저하가 발생하게 됩니다아래 그림과 같이 한번 다운로드 받은 애셋 번들을 하드디스크에 저장하고두 번째 불러올 때는 네트웍이 아닌 하드디스크에서 불러오는 기능이 캐싱입니다.

 

8. 캐싱 기능은 어떻게 사용하나요?

WWW 클래스에서 제공하는 LoadFromCacheOrDownload 함수를 사용하여 애셋번들의 url, 버젼 정보체크섬 정보를 사용하면 자동으로 하드디스크에 지정한 버젼과 url의 애셋번들이 존재하는 경우 하드디스크에서 불러오고 아닌 경우에는 인터넷을 통해서 불러들이게 됩니다.

 

9. 캐싱 기능과 애셋 번들을 같이 활용하고 싶습니다.

WWW.LoadFromCacheOrDownload 함수는 단순하게 제공받은 버젼정보와 URL만 비교해서 애셋번들이 하드디스크에 있으면 로딩없으면 웹에서 다운로드하는 로직을 수행합니다. (내부 로직상 버젼의 숫자는 큰 의미가 없습니다. ) 아래는 애셋번들을 관리하는 간단한 관리자 클래스 예제입니다.

아래는 이를 활용하여 다운로드받는 컴포넌트의 코드입니다.


10. 애셋 번들의 버젼을 관리하여 패치 시스템을 제작하고 싶습니다.

패치 시스템은 위의 기능을 활용하여 직접 만들어야 하는 시스템입니다패치 시스템을 구현하려면 클라이언트 버젼에 따라 다운로드 받아야하는 파일들의 목록과 이들이 직접 비교해서 다운을 받는 시스템을 직접 만들어야 합니다.

하나의 예로 패치 목록을 먼저 다운로드 받은 후에, Cache.IsVersionCached 함수를 사용하여 목록의 각 아이템들이 캐시에 있는지 검사한 후다운로드를 일괄적으로 진행하는 방법이 있습니다이러한 경우 애셋번들의 생성제작 및 관리를 위한 CMS(Contents Management System)시스템이 별도로 필요하며이러한 시스템은 게임을 제작하는 스튜디오 내의 업무 프로세스와 밀접하게 연관되기 때문에 유니티가 별도로 솔루션을 제공하지는 않습니다.

 

11. 다운로드 받은 애셋번들은 어떻게 사용하나요?

애셋번들을 다운로드 받으면 WWW클래스의 assetbundle 프로퍼티를 통해 AssetBundle 클래스를사용하여 애셋번들에 접근하여 원하는 애셋을 런타임에서 로딩하는 것이 가능합니다아래는 관련 소스입니다.


12. 애셋번들을 통해 스크립트 로직을 업데이트 하고 싶습니다.

유니티에서 스크립트 파일은 컴파일 과정을 거쳐 dll로 제작되며이 dll은 항상 초기 설치 파일에 포함되도록 설계되어 있습니다이 dll의 일부분을 애셋번들로 빼는 것은 불가능합니다.

스크립트를 사용하는 프리팹의 에셋번들을 제작하는 경우코드 자체가 저장되는 것이 아니고 컴포넌트의 클래스 레퍼런스 정보 및 설정 정보만 저장됩니다이 정보를 기반으로 프리팹의 스크립트 로직은 메인 프로그램내의 컴파일된 dll가 실행합니다.

다만 dll을 binary 형태로 저장한 후 애셋번들에 포함시키고, 아래 코드와 같이 .NET의 Reflection 기능을 사용하여 실시간으로 불러들이면 스크립트를 사용하는 것이 가능합니다.

하지만 이러한 과정은 복잡하고 불편하며가장 큰 문제는 AOT(Ahead of Time) 컴파일로 동작하는 아이폰의 경우 Reflection 기능에 제한이 있는 관계로 아래의 코드의 사용이 불가능합니다.

서비스 관점에서도 클라이언트는 언제나 최신 버젼 및 최신 로직을 동일하게 유지해야 하기 때문에초기 설치 파일을 업데이트하는 것이 올바른 방법이라는 의견도 있으니 참고하길 바랍니다.

 

13. 결론

지금까지 애셋 번들에 대한 개괄적인 내용을 알아봤습니다애셋번들은 매우 유용한 기능입니다하지만 모든 것을 제공해주는 만능의 툴은 아닙니다애셋번들을 사용하여 대규모 혹은 실시간으로 업데이트 되는 서비스를 만들 때 애셋번들의 특성 및 라이선스 관련된 사항들을 미리 숙지하시고 시스템을 설계하여야 합니다기타 다양한 애셋번들에 대한 활용법 및 추가적인 설명은 아래 유니티 홈페이지 링크를 참고하세요.

http://docs.unity3d.com/Documentation/Manual/AssetBundlesIntro.html

 

 

-----------------------------------------------------------------

 

 

 

Unity3D 예제를 보면 AssetBundle을 만드는 예제가 있는데 xxxxxxxxJavaScript 버전으로 짜여있고, 자세한 설명이 없기 때문에 내용을 한번에 이해하기 힘든 경우가 있다. AssetBundle에대한 이해를 돕고, 조금 더 쉽게 제작할 수 있도록 도움을 주고자 포스트를 작성한다.

 AssetBundle은 Unity3D에서 리소스를 효율적으로 사용하기 위해서 사용하는 리소스의 묶음이다. 안에는 텍스쳐(texture) 메시데이터(mesh), 메트리얼(material) 등 실제 게임에서 사용할 리소스들이 들어갈 수 있다. AssetBundle은 Unity Pro, 혹은 Unity iPhone Advanced 에서만 사용할 수 있다.

 Unity3D에서 AssetBundle을 많이 사용하는 가장 큰 이유는 로딩(다운로드) 용량을 줄이기 위해서이다. 특히 WebPlayer에서는 다운로드 용량을 줄이는건 절대적이다.

 쳅터 형식으로 구성되어있는 게임이 있다. 이 게임에 총 10개의 레벨이 있고 각각의 레벨은 1mb의 리소스들을 가진다고 가정하자. 사용자가 10개의 레벨이 모두 들어있는 파일을 다운받아서 플레이를 하는경우와, 각각의 레벨을 AssetBundle로 만들어놓고 사용자가 다음 레벨로 넘어갈 때 마다 AssetBundle을 다운로드를 받아서 로드하게 만드는 경우중 어느 것이 더 효율적일까?

 당연히 후자쪽이 될 것이다.

 사용자가 처음 게임을 시작해서 5개의 레벨을 진행했다고 가정하면, 전자의 경우는 사용자가 한번에 10개의 레벨 리소스를 모두 받아야 하므로 10mb를 한번에 받아야 한다.
 후자쪽은 1레벨을 할 때는 1레벨에 해당하는 리소스 1mb만 받고, 2레벨로 넘어갈 때 2레벨의 리소스에 해당하는 1mb만 추가로 받으면 된다. 이런식으로 사용자가 한번에 레벨 5까지 진행을 했을 경우 후자의 경우는 5mb만 다운로드 받게 된다. 전체적으로 받아야 하는 다운로드 용량이 전자에 비해서 크게 줄어들게 된다. 물론 모든 레벨을 한번에 진행하게 되면 다운받는 용량은 10mb로 비슷해질 것이다.

 이렇게 AssetBundle을 이용하면 리소스를 효율적으로 관리할 수 있게된다.

 그렇다면 AssetBundle은 어디서, 어떻게 만들까? 찾아본 사람들은 알겠지만, 메뉴어디에도 AssetBundle 이라는 단어는 보이지 않는다.
 자동으로 AssetBundle을 만들 수 있다면 참 좋겠지만, 안타깝게도 AssetBundle을 만드는 과정은 상당히 번거로운 과정으로 수작업의 연속이다. 간단한 AssetBundle을 만들어보자.

 먼저 프로젝트 폴더에 'Editor'라는 폴더를 만들어주자. 이 안에는 실제 게임과는 상관 없이 동작하는 녀석들이 들어가게 된다. 빌드를 해도 Editor안에 있는 녀석들은 빌드파일 안에 포함되지 않을것이다.(아마도) 'AssetBundle을 만드는 스크립트'도 게임과 상관없이 동작하는 녀석들이 되겠다. 스크립트를 새로 만들고 아래처럼 'Editor'폴더안에 넣어두자. 스크립트의 이름은 크게 중요하지 않다.(나의 경우는 C#스크립트로 만들었다.)




 새로  만들어놓은 스크립트를 열어보면 아무것도 작성되지 않은 깔끔한 상태의 스크립트를 볼 수 있다. 이제부터 해야할 일은 이 스크립트안에 AssetBundle을 제작하는 스크립트로 체워넣는 것이다. 일단 아래처럼 스크립트를 작성한다.
 

using UnityEngine;
using System.Collections;
using UnityEditor; //(1)

public class BuildAssetBundles{

    [MenuItem ("Assets/Auto Build Image File")] // (2)
//  @MenuItem("Assets/Auto Build Image File") // @ xxxxxxxxJavaScript

    public static void buildImage(){

        BuildPipeline.PushAssetDependencies(); // (3)

        BuildAssetBundleOptions  options = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets; //(4)
        
        Object[] asset = new Object[3];
        asset[0] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/a02-1.png"); //(5)
        asset[1] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/a03-1.png");
        asset[2] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/b01-1.png");
        
        Debug.Log(asset[0]);
        
        BuildPipeline.BuildAssetBundle(null, asset, "Shared.unity3d", options);    //(6)
//        BuildPipeline.BuildAssetBundle(asset[0], null, "Shared.unity3d", options);    //(7)

        BuildPipeline.PopAssetDependencies(); //(8)
    }
    
}



 AssetBundle을 만드는 스크립트에는 (1)과 같이 UnityEditor 네임스페이스를 써야한다. 이 네임스페이스를 통해서 UnityEditor API를 사용할 수 있는데, (2)와 같은 부분이 UnityEditor API를 사용한 것이다. UnityEditor API를 사용하면 스크립트의 특정 코드를 Unity 의 메뉴에서 수행할 수 있게 만들어준다. 스크립트를 저장하면 'Assets' 메뉴에 'Auto Build Image File' 메뉴가 새로 생긴것을 확인할 수 있다. 이녀석을 눌러 AssetBundle을 생성할 수 있다. 아직 몇가지 할일이 더 남아있으니 성급히 누르지는 말자.

 




 (3)부분은 서로 다은 AssetBundle간의 의존성과 관계되어있다. AssetBundle을 여러개 만들때 상호간에 의존성을 주고 싶다면 BuildPipeline.PushAssetDependencies() 와 BuildPipeline.PopAssetDependencies()를 적절히 잘 사용해서 의존성을 만들어 줄 수 있다. 사용할 때 는 Push와 Pop의 개수가 같아야 한다고 한다. Unity3D공식 사이트에 있는 예제를 보면 보다쉽게 이해를 할 수 있다. (사실 아직은 명확히 이해가 안간다)

 (4)부분은 AssetBundle을 빌드할 때 옵션을 설정해주는 것인데, CollectDependencies 는 AssetBundle로 만들 Asset(리소스)에 의존성을 가지는 GameObject, Component등 모든 것들을 포함해서 같이 빌드 하는 것이고, CompleteAsset 같은 Asset안에 포함되어있는 GameObject, Component들을 포함해서 빌드하는 것이다. 사실 지금 하려고 하는 예제에서는 딱히 필요가 없는 옵션인것 같긴 한데, 일단 넣어두었다.

 (5)에서 실제로 리소스를 가져오는 작업을 한다. (파일 이름과 경로는 구미에 맞게 변경하자!)

 (6)에서 실제 AssetBundle을 만드는 작업을 수행하는데 (7)과 다른점은 (6)에서는 여러개의 리소스를 하나의 AssetBundle에 넣을 때 사용하고, (7)에서는 한번에 하나씩만 넣을 수 있다.

 이렇게 AssetBundle을 만드는 스크립트를 작성을 했으니 이제 해당하는 경로에 리소스를 위치해보자. Resource 폴더를 만들고 이미지 파일을 위치시켰다.
 


 이제 준비가 다 되었으니 'Assets'메뉴에 있는 'Auto Build Image File' 을 눌러보자.


위와같은 압축과정을 거치고 나면 아래와 같이 빌드된 AssetBundle 'Shared.unity3d'를 확인할 수 있다.


이렇게 만들어진 AssetBundle은 WWW클래스를 이용하면 언제든지 가져다 사용할 수 있다.

거듭 이야기 하지만, AssetBundle은 Unity Pro, 혹은 Unity iPhone Advanced 에서만 사용할 수 있다

 

 

-------------------------------------

 

assetBundle.Load("file", typeof(UnityEngine.TextAsset));

-------------------------------------

 

 

 

unity3d 엔진에서 모바일에서 데이터를 쓰려면 결국 Assetbundle을 사용해야 한다.

이유는 바로

용.!

량.!

모바일 기기들은 대부분 App를 다운 받아 설치해서 프로그램을 이용하는데 이때. 보통의 게임은 용량이 크다.

용량이 크면 문제는 다운로드 시간이 많이 걸리고 그러면 유저가 다운받지 않을 확률도 높다고 한다.

그리고 특히 mmorpg나 morpg나 아무튼 rpg를 서비스 하는데 패치 문제도 있고, 여러모로 결국은 assetbundle을 사용해야한다.

 

여기에서 웹에서 쓰는 캐쉬는 논외로 하자 어짜피 이건 유저가 손댈수 없는 영역이다. 유니티에서 알아서 해주는거라 그냥 쓰기만 하면된다. 따로 손댈것도 없다 .

 

그럼 안드로이드 나 아이폰 아이패드 에서는 어떻게 사용해야하나. 만들면된다. 디스크를 읽고 쓸수도 있고

패치  버전 관리도 자체적으로 사용 가능 하다. 이게 유니티에서 제공하는 웹 캐시보다 훨씬 좋다.(진짜 울며 겨자먹기로 웹버전 캐시를 쓰는거지 드럽게 비싸고 성능도 구리고 아무튼 별로다.)

 

assetbundle을 안드로이드 버전이나, ios로 만드는 것은 간단하다.

 

[MenuItem("Assets/Build AssetBundle From Selection Android - Track dependencies")]
 static void ExportResourceToAssetBundle()
 {
  string FilePath = EditorUtility.SaveFilePanel("Export Project Data", "", "NewAssetBundle", "unity3d");
  
  if(FilePath.Length != 0)
  {
   Object[] ProjSelection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
   BuildPipeline.BuildAssetBundle(Selection.activeObject, ProjSelection, FilePath
                , BuildAssetBundleOptions.CollectDependencies 
                | BuildAssetBundleOptions.CompleteAssets
                ,BuildTarget.Android);     
   Selection.objects = ProjSelection;
  }
 } 

가장 간단하게 위의 코드대로 하면 어셋번들을 뽑아 낼수 있다.

여기에서 안드로이드 버전과 ios 버전을 바꾸는건    BuildPipeline.BuildAssetBundle 의 마지막 옵션인

BuildTarget.Android 만 IOS 로 바꾸면된다.

위 스크립트내 용은 메뉴의 asset 항목에 Build AssetBundle From Selection Android - Track dependencies 라는 항목을 추가하고, 사용 방법은  Assetbundle을 만들 프리팹을 선택한뒤 해당 항목을 선택하면 어셋번들을 만들 경로와 파일명을 설정한뒤 생성한다 .

assetbundle을 만들때 확장자는 마음대로 설정할수 있다. 위 코드에선 기본 확장자를 unity3d 로 설정 했다.

 

그럼 이렇게 만든 어셋번들은 어떻게 사용 하는가 .

Assetbundle 은 서버에 일단 올려두고 다운받아 사용하는게 일반적이다.

물론 app 내부에 넣고 읽어도 되지만 그러려면 ..뭐하러 이짓을 하나..'';

아무튼 assetbundle을 올려둔뒤 읽어 들이려면 WWW 클래스를 사용한다.

사용법은

WWW www = new WWW(번들주소);

여기서 번들주소는 http:// 로 시작하면 웹에서 읽어 들이고 file://로 시작하면 내부 경로에서 읽어 들인다 .

ex > 웹에서 읽어 들일때.

WWW www = new WWW(http://daum.blog.com/xxxxx"); 뭐 이런식으로

ex > 내부 경로에서 읽어 들일때.

WWW www = new WWW(file://d:/assetbundle/aaa.assetbundle"); 뭐 이런식으로

사용한다.

 

%WWW 클래스는 꼭 assetbundle을 다운받는 용도가 아니다 유니티에서 파일을 로드하는 클래스다. txt를 읽어 들이고 할때도 저 걸 쓰면 된다.%

private IEnumerator LoadAssetbundle()
{
  WWW www = new WWW(stPatchfileListUrl);
        yield return www;

..

...

....

}

위 함수는 이런식으로 해서 읽어 들이고 사용하는데 IEnumerator 는 나중에 시간나면 포스팅 할꺼다. 그건 지금 포스팅 주제가 아니니까.

그런데 왜 저걸 다운로드라고 한게 아니고 읽어들였다라고 한거냐면 다운로드라고 하면 파일이 생성 되야 하는데 저건 메모리에 올라가만 있는 거다 저걸 파일로 남겨야 로컬경로에 어셋번들이 남아있고 다운도르가 된거다.

 

파일 저장은 ? 그냥 C#에서 StreamWriter/StreamReader를 이용해서 읽고 쓰면된다.

이 부분에서 주의 할점은 ios는 Application.dataPath 안에 /Documents/ 안에다만 저장할수 있다는것

안드로이드는 sd카드에 받아야 대용량도 저장해둘텐데 이때 경로는 mnt/sdcard/ 라는 것이다 .

이것만 알면 파일 저장은 C#에서 제공하는 함수로 쓰면 된다.

 

읽어 들인 Assetbundle은 GameObject.Instantiate 함수로 프리팹을 게임에 생성한다던지

string patchlist = www.data; 이런식으로 텍스트 파일일경우 내부 데이터를 쓴다던지 하면된다.

각각의 Assetbundle의 사용은 각 데이터의 유형에 따라 다르니 그것에 맞춰 쓰면 되겠다.

 

%주의

데이터를 쓰기전에 꼭 if (www!=null && www.isDone) 이런 식으로 데이터 로드가 끝났는지 확인 하는게 좋다.

var flashVersion = parent.swfobject.getFlashPlayerVersion(); if(typeof flashVersion != "undefined" && typeof flashVersion.major != "undefined" && flashVersion.major >= 10 && typeof ExifViewer != "undefined"){ var getTxImages = function () { var result, txImages, images, i, len, img; result = []; images = []; txImages = document.body.getElementsByTagName("img"); len = txImages.length; for (i = 0; i < len; i += 1) { img = txImages[i]; if (/tx\-daum\-image|txc\-image/.test(img.className)) { images.push(img); } } return result; }; var txImages = getTxImages(); ExifViewer.load({ serviceName: "blog", images: txImages, showAllItem: false, imageViewer: { templateVal‍ue: { blogid: encodeURIComponent(BLOGID), articleurl: encodeURIComponent("http://blog.daum.net/kb082/11745799") }, photoList: { photoListProtocol: "blogphotolistselect", photoListDataFromUrl: "http://blog.daum.net/_blog/api/PhotoListSelectImageViewer.do?blogid={blogid}&articleurl={articleurl}&imageurl={imageurl}" }, groupList: { groupListProtocol: "blogcatelist", groupListDataFromUrl: "http://blog.daum.net/_blog/api/CategoryList.do?blogid={blogid}" }, data: { count: txImages.length, getViewingUrl: function (index) { return txImages[index].src; } } } }); }

 

 

어셋번들에서 사용자 정의 바이너리 파일 로딩

string url = "http://www.mywebsite.com/mygame/assetbundles/assetbundle1.unity3d";
IEnumerator Start () {
    // Start a download of the given URL
    WWW www = WWW.LoadFromCacheOrDownload (url, 1);

    // Wait for download to complete
    yield return www;

    // Load and retrieve the AssetBundle
    AssetBundle bundle = www.assetBundle;

    // Load the TextAsset object
    TextAsset txt = bundle.Load("myBinaryAsText", typeof(TextAsset)) as TextAsset;

    // Retrieve the binary data as an array of bytes
    byte[] bytes = txt.bytes;
}

 

--------------------------------------------------------

 

 

AssetBundle로 mp3파일 로딩하기

 

일단 음원이 들어가 있는 상태에서 AssetBundle을 만듭니다.


오늘안 사실 Editor란 폴더를 쓰면 , 빌드와 무관한 영역이 생긴다는것 //





using UnityEngine;

using System.Collections;

using UnityEditor;


public class BuildAssetBundles  

{

[MenuItem ("Assets/ Build Sound File")]

public static void buildAsset()

{

BuildPipeline.PushAssetDependencies();

// option

BuildAssetBundleOptions option = BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets;

Object[] mp3Asset = new Object[2];

// where

mp3Asset[0] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/se09.wav");

mp3Asset[1] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/se35.wav");

/* I&U new song 5/11 !!*/ sorry..

BuildPipeline.BuildAssetBundle(null,mp3Asset,"ToyCat_SharedData.unity3d",option);

BuildPipeline.PopAssetDependencies();

}

}


그러면 다음과 같은 *.unity3d 형식의 파일이 생겨요.








그리고 다음과 같은 소스로 에셋번들을 불러와서 음악을 재생시킵니다.

using UnityEngine;
using System.Collections;


public class DownLoad : MonoBehaviour {
public AssetBundle bundle;
public AudioClip Au;
public AudioSource As;
public string url = "ToasyCssat_SharedData.unity3d";
IEnumerator Mp3DownLoad()
{
// only Local
string patch = "file://"+Application.dataPath+"/../"+url ;
Debug.Log(patch);
WWW www = WWW.LoadFromCacheOrDownload(patch, 1);
yield return www;
bundle = www.assetBundle;
Au = bundle.Load("se09",typeof(AudioClip) ) as AudioClip;
As = this.gameObject.AddComponent<AudioSource>();
As.clip = Au;
As.loop = true;
As.Play();
}
public void OnGUI()
{
if ( GUI.Button(new Rect(0,0,100,100) , "Load") )
{
StartCoroutine("Mp3DownLoad");
}
}
// Use this for initialization
void Start () {

}
// Update is called once per frame
void Update () {
}
}

 

 

--------------------------------------------------------

AssetBundle을 사용한 NGUI prefab로드

크게 두 부분으로 나눌 수 있는데, AssetBundle을 사용하여 .unity3d파일을 생성하는 부분과, 생성된 파일을 읽어와서 사용하는 부분입니다. 여기에 .unity3d파일을 다운로드하여 읽어오는 것까지 추가해 보겠습니다.



1. AssetBundle의 생성


먼저 Editor폴더와 BuildAssetBundles 클래스를 추가합니다.


 

BuildAssetBundles 클래스에서 menu item을 추가하여 .unity3d파일을 생성하게 됩니다.



using UnityEngine;

using System.Collections;

using UnityEditor;


public class BuildAssetBundles

{

    [MenuItem("Assets/Build AssetBundles")]

    public static void buildAssetBundles()

    {

        BuildPipeline.PushAssetDependencies();


        //BuildAssetBundleOptions options = 

        //    BuildAssetBundleOptions.CollectDependencies

        //    | BuildAssetBundleOptions.CompleteAssets;

        BuildAssetBundleOptions options =

            BuildAssetBundleOptions.CollectDependencies;


        Object[] asset = new Object[1];

        asset[0] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/Test/ABTestPrefab.prefab");


        Debug.Log(asset[0]);


        BuildPipeline.BuildAssetBundle(null, asset, "assetbundle.unity3d", options);

        //BuildPipeline.BuildAssetBundle(asset[0], null, "assetbundle.unity3d", options);


        BuildPipeline.PopAssetDependencies();

    }

}



코드를 저장하고 나면 [MenuItem("Assets/Build AssetBundles")]코드로 인해 Unity의 메뉴- Assets에 Build AssetBundles 항목이 추가된 것을 볼 수 있습니다.




여기서 주의할 점은 BuildAssetBundleOptions을 꼭 BuildAssetBundleOptions.CollectDependencies 으로 설정해야 합니다prefab은 텍스쳐, 스크립트 등 다른 요소들과 의존성을 가지고 있기 때문에, 이를 모두 포함시키도록 하는 옵션입니다.

.prefab파일은 수 KB정도의 크기를 갖지만 .unity3d 파일로 만들고 나면 관련된 요소들을 모두 포함한 크기를 가진 .unity3d파일이 생성됩니다.


만약 BuildAssetBundleOptions.CompleteAssets으로 설정하면 수 KB의 크기를 가진 .unity파일이 생성되고, 이를 로드했을 시 원하는 결과가 나오지않을 수 있습니다.


asset[0] = AssetDatabase.LoadMainAssetAtPath("Assets/Resources/Test/ABTestPrefab.prefab"); 와 같이 원하는 resource들을 계속해서 추가할 수 있습니다.


BuildPipeline.BuildAssetBundle(null, asset, "assetbundle.unity3d", options);

BuildPipeline.BuildAssetBundle(asset[0], null, "assetbundle.unity3d", options);

위 두 코드의 차이는 전자는 asset들을 모두 묶어서 AssetBundle을 생성하는 것이고, 

후자는 하나씩 MainAsset으로 생성하는 것이며,


BuildPipeline.BuildAssetBundle(asset[0], asset, "assetbundle.unity3d", options); 와 같이 사용할 수도 있습니다. 


이는 로드할 시에 차이를 가지게 되며, 로드하는 부분에서 설명하도록 하겠습니다.


Unity의 메뉴- Assets에 Build AssetBundles 을 실행하면 assetbundle.unity3d파일을 생성하게 됩니다. 아래는 생성된 파일입니다.

 


 assetbundle.unity3d




2. AssetBundle의 사용


위에서 생성한 assetbundle.unity3d 파일을 WWW클래스로 다운로드하여 저장하고, 이를 로드하여 prefab을 Instantiate을 합니다.



    void Awake()

    }

         StartCoroutine("LoadBundle");

    }


 IEnumerator LoadBundle()

    {

        string bundlePath = "http://lazli.tistory.com/attachment/cfile26.uf@2522EE4451CAA1FA06B1C9.unity3d";

        string docPath = Application.dataPath + "/Resources/Bundle/assetbundle.unity3d";

        Debug.Log("docPath=" + docPath);


        FileInfo fInfo = new FileInfo(docPath);

        WWW www;

        if (!fInfo.Exists)

        {

            www = new WWW(bundlePath);

            Debug.Log(Application.dataPath);

            yield return www;


            if (www.isDone)

            {

                FileStream fs = new FileStream(docPath, FileMode.CreateNew);

                BinaryWriter w = new BinaryWriter(fs);

                w.Write(www.bytes);

                w.Close();

                fs.Close();

            }

        }


        www = new WWW("file:///" + docPath);

        Debug.Log(Application.dataPath);

        yield return www;

        if (www.isDone)

        {

            //GameObject obj = Instantiate((GameObject)(www.assetBundle.mainAsset)) as GameObject;

            GameObject obj = Instantiate(www.assetBundle.Load("ABTestPrefab", typeof(GameObject))) as GameObject;

            


            Vector3 localPos = obj.transform.localPosition;

            Vector3 localScale = obj.transform.localScale;


            obj.transform.parent = GameObject.Find("Anchor").transform;


            obj.transform.localPosition = localPos;

            obj.transform.localScale = localScale;

        }

    }


먼저 assetbundle.unity3d파일이 존재하는지 확인하고 존재하지 않으면, WWW 클래스를 사용하여 이를 다운로드하여 로컬에 저장하고, 저장된 파일을 로드하여 Instantiate합니다.


GameObject obj = Instantiate((GameObject)(www.assetBundle.mainAsset)) as GameObject;

GameObject obj = Instantiate(www.assetBundle.Load("ABTestPrefab"typeof(GameObject))) as GameObject;

위 두 코드의 차이는 AssetBundle을 생성할 시에 MainAsset으로 지정한 Asset을 Load할 시에는 전자를, 

이름으로 찾아서 Load할 시에는 후자를 사용합니다.


위 코드의 실행화면입니다.




위 예제코드에서는 첨부된  assetbundle.unity3d 파일을 사용하게 구성되었으므로 그대로 복사하여 사용하더라도 테스트가 가능합니다.

 

 

--------------------------------------------------------

Assetbundle 을 이용한 업데이트 시스템

 

http://answers.unity3d.com/questions/55512/Does-AssetBundle-on-Android-work-well-.html     : Android Asset Bundle

 

다음과 같이 에디터 오퍼레이션으로  Assetbundle 을 만드는데...

BuildPipeline 에서 무조건  플랫폼을 정해 줘야 한다.

 

 


//#if !UNITY_ANDROID

using UnityEngine;
using UnityEditor;
public class ExportAssetBundles { 
    [MenuItem("Assets/Build AssetBundle From Selection - Track dependencies")]  
    static void ExportResource () { 
        // Bring up save panel 
        string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
        if (path.Length != 0) {
            // Build the resource file from the active selection. 
            Object[] selection = Selection.GetFiltered(typeof(Object), SelectionMode.DeepAssets);
            BuildPipeline.BuildAssetBundle(Selection.activeObject, selection, path, BuildAssetBundleOptions.CollectDependencies | BuildAssetBundleOptions.CompleteAssets,BuildTarget.Android);   // 
            Selection.objects = selection;   
        } 
    } 
    [MenuItem("Assets/Build AssetBundle From Selection - No dependency tracking")] 
    static void ExportResourceNoTrack () {
        // Bring up save panel  
        string path = EditorUtility.SaveFilePanel ("Save Resource", "", "New Resource", "unity3d");
        if (path.Length != 0) { 
            // Build the resource file from the active selection. 
            BuildPipeline.BuildAssetBundle(Selection.activeObject, Selection.objects, path); 
        }  
    }
}
//#endif

 

 

 


        if (m_goPatchingScenePrefab.GetComponent<PatchingTesting>().m_nVersion < 2)
        {
            // Patching Update.......


           // WWW www = new WWW("http://192.168.0.40/ppp2.unity3d");

 

 

           WWW www =  WWW.LoadFromCacheOrDownload("http://192.168.0.40/ppp2.unity3d",2);
           

            //            www.uploadProgress

            /*while(!www.isDone)
            {


                        yield return 0;
            }
            */
            //  yeild retrun www.;


            yield return www;

 

 


            Debug.Log("Down Load Done!!");
            //    Instantiate(www.assetBundle.mainAsset);
            m_bDownloaded = true;
            //        Instantiate(www.assetBundle.mainAsset);

            //    www.assetBundle.LoadAll();


            m_goPatchingScenePrefab = (GameObject)www.assetBundle.Load("PatchingContests");

 

 

        }
       


            Instantiate(m_goPatchingScenePrefab);

 

 

 

 

IIS  설정 후....

 

1.  MIME 에 ~.unity3d를 추가 해야 한다.

2. crossdomain.xml 이 최 상위에 있어야 하는데...이건 만들어야 한다.

 

 

--------------------------------------------------------

데이터 패스 정리

 

각 플랫폼별로 Application 클래스의 프로퍼티들이 지정하는 경로와 엑세스에 대해 정리해보았습니다.
테스트한 기기에 따라 결과가 다를 수 있는데, 미진한 부분들은 댓글 남겨주시면 감사하겠습니다.

[윈도우 에디터]
Application.persistentDataPath : 사용자디렉토리/AppData/LocalLow/회사이름/프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 프로젝트디렉토리/Assets
Application.streamingAssetsPath : 프로젝트디렉토리/Assets/StreamingAssets
파일 읽기 쓰기 가능


[윈도우 응용프로그램]
Application.persistentDataPath : 사용자디렉토리/AppData/LocalLow/회사이름/프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 실행파일/실행파일_Data
Application.streamingAssetsPath : 실행파일/실행파일_Data/StreamingAssets
파일 읽기 쓰기 가능

[맥 에디터]
Application.persistentDataPath : 사용자디렉토리/Library/Caches/unity.회사이름.프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 프로젝트디렉토리/Assets
Application.streamingAssetsPath : 프로젝트디렉토리/Assets/StreamingAssets
파일 읽기 쓰기 가능

[맥 응용프로그램]
Application.persistentDataPath : 사용자디렉토리/Library/Caches/unity.회사이름.프로덕트이름
파일 읽기 쓰기 가능
Application.dataPath : 실행파일.app/Contents
Application.streamingAssetsPath : 실행파일.app/Contents/Data/StreamingAssets
파일 읽기 쓰기 가능

[웹 플랫폼]
웹에서는 명시적인 파일 쓰기 불가능. 애셋번들로 해야함
Application.persistentDataPath : /
Application.dataPath : unity3d파일이 있는 폴더
Application.streamingAssetsPath : 값 없음.

[안드로이드 External]
Application.persistentDataPath : /mnt/sdcard/Android/data/번들이름/files
파일 읽기 쓰기 가능
Application.dataPath : /data/app/번들이름-번호.apk
Application.streamingAssetsPath : jar:file:///data/app/번들이름.apk!/assets 
파일이 아닌 WWW로 읽기 가능

[안드로이드 Internal] 
Application.persistentDataPath : /data/data/번들이름/files/
파일 읽기 쓰기 가능
Application.dataPath : /data/app/번들이름-번호.apk
Application.streamingAssetsPath : jar:file:///data/app/번들이름.apk!/assets
파일이 아닌 WWW로 읽기 가능

[iOS]
Application.persistentDataPath : /var/mobile/Applications/프로그램ID/Documents 
파일 읽기 쓰기 가능
Application.dataPath : /var/mobile/Applications/프로그램ID/앱이름.app/Data
Application.streamingAssetsPath : /var/mobile/Applications/프로그램ID/앱이름.app/Data/Raw 
파일 읽기 가능, 쓰기 불가능

Application.temporaryCachePath 도 있습니다 

윈도우 : %LocalAppData%/Local/Temp/Temp/%Company%/%Product% 
Android Internal : /data/data/%BundleIdentifier%/cache 
Android External : /mnt/sdcard/Android/data/%BundleIdentifier%/cache 
iOS : /var/mobile/Applications/프로그램ID/Library/Caches 

 

 

 

 

Editor
- Application.dataPath /*project*/Assets

 

 

 


 

Web Player

 System.IO.Path.GetFullPath(".")

 %Project%

 Application.dataPath

 %Project%/Assets

 Application.persistentDataPath

 %userprofile%\AppData\LocalLow/%Company%/%Product%

 Application.streamingAssetsPath

 %Project%/Assets/StreamingAssets

 Application.temporaryCachePath

 %LocalAppData%/Local/Temp/Temp/%Company%/%Product%



Android

 System.IO.Path.GetFullPath(".")

 /

 Application.dataPath

 /data/app/%BundleIdentifier%.apk

 Application.persistentDataPath

 /data/data/%BundleIdentifier%/files

 Application.streamingAssetsPath

 jar:file:///data/app/%BundleIdentifier%.apk/!/assets

 Application.temporaryCachePath

 /data/data/%BundleIdentifier%/cache


Android sdcard

 

 Application.persistentDataPath

 /mnt/sdcard/Android/data/%BundleIdentifier%/files

 Application.streamingAssetsPath

 jar:file:///data/app/%BundleIdentifier%.apk/!/assets

 Application.temporaryCachePathz

 /mnt/sdcard/Android/data/%BundleIdentifier%/cache


iOS

 System.IO.Path.GetFullPath(".")

 /

 Application.dataPath

 %ProvisioningProfile%/%BundleIdentifier%.app/Data

 Application.persistentDataPath

 %ProvisioningProfile%/Documents

 Application.streamingAssetsPath

 %ProvisioningProfile%/%BundleIdentifier%.app/Data/Raw

 Application.temporaryCachePath

 %ProvisioningProfile%/Library/Caches

 

// ** iPhone device
- Application.dataPath /var/mobile/Applications/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Assets
- Application.persistentDataPath : /var/mobile/Applications/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Documents
- Application.temporaryCachePath : /var/mobile/Applications/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/Library/Caches
- iPhpone Simulator Path : /Users/<Name>/Library/Application Support/iPhone Simulator/4.1/Applications/<GUID>/
     
// ** Android
- Application.dataPath /mnt/asec/com.xxx.xxx/pkg.apk
- Application.persistentDataPath /data/data/com.xxx.xxx/files/
- Application.temporaryCachePath /data/data/com.xxx.xxx/cache/
* sdcard 사용시.
- Application.persistentDataPath /mnt/sdcard/Android/data/com.xxx.xxx/files/
- Application.temporaryCachePath /mnt/sdcard/Android/data/com.xxx.xxx/cache/

 

 

 

 

 

 

 

에셋 번들 사용하기 첫 번째 - 생성하기

 

http://unitystudy.net/bbs/board.php?bo_table=writings&wr_id=62&page=2

위 링크에 가보면, 에셋 번들에 대한 설명이 자세히 나와 있으니, 에셋 번들이 뭔지에 대한 부분은 위에서 확인하시면 될 것 같습니다.

 

 

그럼 이번에는 에셋 번들 사용하는 방법에 대해서 적어 볼 까 합니다.

 

에셋 번들을 사용하려면 만들어야 하겠죠?

그럼 만드는 방법부터 알아보겠습니다.

(절대! 어렵지 않습니다 - 유니티 레퍼런스 예제를 찾아서 복사/붙여넣기만 하면 됩니다)

 

일단 만들기는 유니티 에디터 에서 합니다.

 

http://docs.unity3d.com/Documentation/ScriptReference/BuildPipeline.BuildAssetBundle.html

 

위 링크에 가시면, 아래쪽에 c#으로 된 코드가 있습니다.

에디터에서 C# 스크립트를 하나 만들고 위의 코드를 붙여넣기 합니다. 

 

(물론 클래스 이름과 스크립트 이름이 같아야 하는건 아시죠? 예제에 나온 클래스 이름은 “ExportAssetBundles”니까 스크립트 이름도 이와 같이 해주시거나, 이 이름이 맘에 안드는 경우에는 둘다 바꾸시면 됩니다.)

 

글만 보시면, 지겨우시니까 한번 해보죠. 

보시면 정말 예제 코드가 있습니다.


1.png


그대로 긁어다가..붙여넣기 합니다.

 


2.png


 

보시면 알겠지만, 그대로 가져다가 붙였습니다.

( 쉽죠? 어렵지 않습니다 에셋번들 하하하...(   --) ) 

 

그런데 여기서 주의해야 합니다.

C#스크립트 위치가 중요합니다 !!!!!!!

 

유니티에는 “에디터” 스크립트라는 게 있습니다.

using UnityEditor 를 사용하면 에디터 스크립트라고 보시면 됩니다.

이 에디터 스크립트는 반드시! “Editor” 폴더 아래 놓으셔야 합니다. 그래야만 동작을 하고, 

그래야만 에러를 만들지 않습니다. 

( 프로젝트 폴더 안에 위치가 어디든 이름이 “Editor”로 된 폴더를 만드시고 그 아래에 두시면 됩니다. )

 

보여드리지요..


3.png

위 그림을 보시면 Scripts -> Editor 의 형태로 “Editor” 폴더가 위치해 있는데, 이처럼 위치는 상관이 없지만 “Editor” 이름의 폴더안에 두셔야 동작을 합니다.

 

이제 사용할 준비가 되어 있으니, 써봐야 겠지요..?

 

에디터의 실행버튼을 누르시면...안되구요..

 

에디터 메뉴의 Assets 메뉴를 누르시면 아래쪽에 

Build AssetBundle From Selection - Track dependencies

라는 메뉴가 보이실 거에요. 

 

에셋 번들로 만드려는 재료 (텍스쳐, 프리펩 등)를 클릭하신 상태에서 위의 버튼을 누르면, 


4.png

에셋번들을 저장할 위치를 고르는 창이 뜨고 저장을 하시면 드디어..

에셋 번들이 만들어졌습니다.


5.png

 

저는 위에 보이는 Assetbundles라는 폴더 아래에 번들을 하나 만들어 두었습니다.

 

에셋 번들은 이렇게 에디터에서 만드시고 게임 내에서 동적으로 로드를 해서 사용하시면 됩니다. 

 

어렵지 않죠?

 

 

참고로 말씀 드리면, 에셋번들은 zip같은 일종의 압축파일 방식이라고 생각하시면 됩니다.

유니티 내부에서 관리 하기 좋은 방식으로 다루기 때문에, 에셋 관리 측면에서 좋습니다. 

 

게임 빌드 사이즈를 줄이고, 서버 등에 에셋 번들을 올려놓고 다운로드 받도록 할 수 있습니다. 

(에셋 번들로 만든 에셋 들을 프로젝트 파일에서 빼고 서버에 올려두면 apk 등 빌드 사이즈를 줄일 수 있겠죠?)

물론 모바일에서도 동작을 아주 아주 잘 합니다.

 

이제 생성을 했으니, 불러 와야 겠지요?

불러서 사용하는 부분을 이어서 올려 보겠습니다.

 

 

에셋 번들 사용하기 두번째 -

 

지난번엔 에셋 번들을 만드는 방법에 대해 알아보았는데요..

 

아마 많은 분들이..이거뭐 아는 거 아니야..하고 생각하셨을 거라 생각은 됩니다..

 

그래도 다시 확인 한다는 차원에서..ㅎㅎ

 

 

그럼 이번에는 만들어놓은 에셋번들을 로드하는 방법에 대해 알아보도록 하지요.

 

먼저 로드할 위치가 두 가지가 있습니다. 하나는 "서버", 또 하나는 "로컬". 

 

서버는 다들 아시겠지만 현재 프로그램이 실행되고 있는 PC가 아닌 다른 PC에서 로딩을 하는 경우는 모두 서버에서 부른다 라고 할 수 있겠네요.

(엄밀히 말하면 서버는 그렇지 않을 수도 있겠지만, 기본적으로 다른 컴퓨터에서 로딩을 하는 경우에는 웹을 거쳐야 하기 때문에 서버라고 했습니다.)

 

그리고 두 번째는 프로그램이 실행되고 있는 PC 또는 모바일장치의 디스크에서 불러오는 방법입니다.

 

 

그럼 이번에는 로컬에서 불러오는 방법부터 알아보겠습니다.

 

http://docs.unity3d.com/Documentation/Manual/DownloadingAssetBundles.html

위 링크에 가보시면, 에셋번들을 다운로드 하는 방법이 두 가지가 나옵니다.


1.png


첫 번째 코드..

 


2.png


 

두 번째 코드..

 

일단 첫 번째 방법으로 먼저 로딩을 해보겠습니다. 

(두 방법의 차이점이 무엇인지는 조금 있다가 설명해 드리지요..)

 

코드를 아주 약간만 수정해서 로딩 해보겠습니다.


3.png


 

우리가 만든 에셋 번들은 현재 서버에 없습니다.

바로 "로컬"에 있지요. 저는 프로젝트 폴더 -> Resources -> Assetbundles 폴더 안에 Test.unity3d 라는 이름으로 에셋번들을 저장했었지요.

바로 이 경로에서 불러옵니다.

 

그래서 path 변수에 이 경로를 넣어 줍니다. 

(혹시나 Path.combine 함수나, Application.dataPath 가 무얼 의미하는 지 모르겠다 싶으신 분들은 구글님께 물어보면 친절히 답해줍니다..)

 

그리고 OnGUI를 이용해서 버튼을 하나 그리고, 이 버튼을 누르면 LoadBundle() 함수를 호출합니다.

여기까지는 어렵지 않지요? ㅎㅎ StartCoroutine 으로 LoadBundle() 함수를 호출하는 것은 LoadBundle() 함수가 IEnumerator를 반환하기 때문입니다.

(IEnumerator를 반환하면 StartCoroutine 으로 부른다 라고 알고 계시면 됩니다. - 물론 다양하게 사용이 가능합니다 이 IEnumerator라는 놈은요..하지만 

여기서는 이렇게만 알아두셔도 괜찮습니다.)

 

네 그러면 LoadBundle()이 뭐하는 놈인지 알아보지요.

먼저 WWW 변수를 초기화 합니다. 초기화 하는데 변수로 위에서 설정한 path 변수를 이용해서 초기화를 합니다.

 

여기서 주의할점!! 우리는 지금 "로컬"에서 불러오고 있습니다 웹이 아닙니다!! 이런경우, 앞에다가 "file://"를 추가해 줘야 합니다.

그래야 유니티가 아..지금 경로가 로컬이구나..하고 알아먹습니다. 우리는 친절하게 이렇게 해줘야 합니다. "file://"를 붙여주지 않으면, 유니티는

아..이건 웹이구나. 하고 알아먹어서..찾지 못해요..

 

그리고 나서 초기화 될때까지 기다립니다! 이게 그 다음줄인 yield return asset; 입니다.

(yield 문도 여러가지로 쓸 수 있지만 공식처럼 외우셔도 에셋번들 로딩하는 데에는 전혀! 지장이 없습니다.)

 

네 그리고 나서, 제가 에셋 번들로 만들어 놓은 프리펩을 Instantiate 함수로 불러옵니다. 그러면 화면에 나타나겠죠 ㅎㅎ

(제가 에셋 번들로 만들었던 프리펩은 그냥 단순히 Plane 오브젝트 였습니다.)

 

그러면 결과 화면을 보겠습니다.


4.png


 

이건 부르기 전입니다.

 

실행하고 불러보겠습니다.


5.png


 

짠~Plane 오브젝트가 나왔지요? ㅎㅎ 게임 뷰 왼쪽 위에 보시면 작게 버튼이 하나 있습니다.

이 버튼을 누르면 위에서 좀 길게 설명한 루틴을 거쳐서 Plane 오브젝트를 화면에 띄웁니다.

 

예제니까..전혀 감흥이 없겠지만, 다양한 곳에서 활용이 가능합니다.

씬이 전환될때 터레인과 적 에셋 들을 불러온다 던지, 여러개의 에셋 번들을 순차적으로 불러온다던지, 에셋번들을 활용할 수 있는 상황은 무궁무진 합니다.

 

무엇보다 "효율적" 입니다. 

(왜! 효율적이냐! 라고 되물으신다면...유니티에서 좋아하는 타입입니다..라고 대답할 수 밖엔 없지만..)

 

 

그럼 이제 두 번째 방법은 뭐가 다른지에 대해 설명해 드릴게요.

 

가장 큰 차이점은 "버젼 관리"에 있습니다.

 

설명이 길면 지루해 지니까 간단히만..(노력해보겠습니다 간단하게..) 설명하겠습니다.

 

예를 들어보지요..

A라는 에셋 번들이 있습니다. 그런데 이 A라는 에셋 번들을 씬이 시작될 때 서버에서 불러옵니다.

 

이런 상황이라면, 매번 서버에서 불러오는 것 보다는, 프로그램이 실행되고 있는 PC 또는 모바일장치의 디스크에서 불러오는 것이 빠르겠죠?

아무리 웹이 빠르다고 한들, 디스크에서 불러오는 것보다 빠르지는 않으니까요.

이런 상황이라면 "다운로드가 되어 있는 에셋 번들은 버젼을 확인해서 디스크에서 불러온다" 이 것이 가장 큰 차이점입니다. 

 

에셋 번들을 다운로드 하면 기본적으로 PC 또는 모바일 장치에 어딘가에 이 에셋번들을 저장시켜 놓습니다.

그리고 다음에 같은 에셋 번들을 다운로드 받아야 하는 상황이 오면 이 에셋번들이 디스크에 있는지 확인을 하고 있으면 디스크에서 불러오고, 

없으면 서버에서 다운로드 받도록 할 수 있습니다.

 

두 번째 방법이 바로 이 방법 입니다.

 

여기까지만 읽으셔도 됩니다. 나중에 또 설명을 하고 예제를 들어 볼텐데, 혹시나 더 궁금하신 분들을 위해서 아래 더 자세히 설명할게요.

(아래 내용은 궁금하신 분들만 읽어주세요 ㅎㅎ 지루할 수 있습니다;;;)

 

=======================================================================================================================================

 

WWW.LoadFromCacheOrDownload() 함수는 인자를 두개 받습니다. 

하나는 에셋번들 경로 (이건 위에 방법에서 넣어준 값과 동일합니다.)

또 하나는 "버젼" 입니다.


이 버젼이라는 건 WWW.LoadFromCacheOrDownload() 함수를 호출할때 결정됩니다.

예를 또 들어볼까요?


A라는 에셋 번들을 "처음" 로딩합니다.

이때 WWW.LoadFromCacheOrDownload("A", 1); 이라고 호출을 하면, 

유니티는 아까 말씀드린 에셋 번들 다운로드하는 디스크 위치에서 "버젼이 1이고 이름이 A인 에셋번들"이 있는지 찾아봅니다.


지금은 처음로딩하니까 없겠지요 당연히..ㅎㅎ

이 경우에는 서버에서 받아옵니다. 받아와서 그 어딘가에 저장해 둡니다. 이때 버젼은 1이 겠지요.


다음에 같은 방법으로 불러옵니다. WWW.LoadFromCacheOrDownload("A", 1);

이번에도 찾습니다. 아! 이번에는 처음이 아니니까 있겠지요? 그러면 디스크에서 불러옵니다.

(처음보다는 빠르겠죠? 디스크에서 불러오니까요 ㅎㅎ)


그럼 조금 상황을 조금 꼬아 보겠습니다.

(여러분들의 이해를 돕기 위함입니다....)


변화가 전혀 없는 "같은 A" 에셋 번들을 이번에는 WWW.LoadFromCacheOrDownload("A", 2); 라고 호출을 해봅니다.

위에서 "A"는 이미 그 어딘가에 받아 두었죠.

그러면 이번에도 디스크에서 불러올까요? 


정답은 "아닙니다" 입니다. 왜냐하면 WWW.LoadFromCacheOrDownload("A", 2); 라고 호출하면, 이번에도 유니티는 찾아봅니다.

"버젼이 2이고 이름이 A인 에셋번들" 이 있는지, 이름이 "A"는 있는데 버젼이 1이네 하고 새로 받아 옵니다.

그리고는 그 어딘가에 버젼2라고 또 저장합니다.


이제좀 감이 오시나요? 같은 에셋번들이라고 해도 버젼이 다르면 새로 불러올 수 있습니다. 물론 이렇게 하지는 않겠지요? 

그러면 어떤 상황에서 버젼이 유용하게 쓰이느냐?하면..


A라는 에셋번들을 조금 수정했습니다. 적 캐릭터의 공격력이 늘어났다던지, 점프하는 동작이 추가되었다던지 하는 변화가 있을 수 있겠지요..

이런 경우라면 A 번들이 디스크에 있다해도 변화가 있고나서 처음에는 다시 다운로드 해야 겠지요?


이런경우에 버젼을 바꿔 줍니다. 

그러면 유니티가 버젼과 이름을 매칭해서 찾아보고 없기 때문에 다시 다운로드 하게 됩니다.



그러면 정리를 간단히 해보겠습니다.


첫번째 방법은 "무조건" 불러온다 입니다.

간단히 에셋 번들을 불러오는 상황이라면 이렇게 하시면 됩니다.


두번째 방법은 "버젼과 이름이 디스크에 있는지 확인하고 있으면 디스크에서 없으면 서버에서 받아온다" 입니다.


차이점을 아셨지요? 


그런데 여기서 아주아주 중요한 점을 언급해야 겠습니다.


두번째 방법을 사용할 때, 유니티가 그 어딘가에서 "버젼과 이름을 비교해본다" 라고 말씀드렸죠? 

근데 이 정보들을 항상 가지고 있지 않습니다 유니티는. 그래서 유니티한테 그 정보를 모으는 시간을 줘야합니다.


WWW.LoadFromCacheOrDownload("A", 1);이렇게 바로 호출하기 전에!!!! 위에 코드에서 보여주듯이, 

while(!Caching.ready)

yield return null;

요로코롬 Caching.ready 가 true를 반환할때 까지 기다려 줘야 합니다.

이 시간이 바로 유니티한테 위에서 말한 정보를 다 모을때 까지 기다려주는 시간입니다.


이 점만 주의를 하시면 문제없이 동작을 할겁니다 하하하.



사실 이 두가지 방법을 알고계시는 분들은 많을 거라 생각이 되는데 ! 제가 방금 말씀드린 이 부분을 모르고 계시는 분들도 상당하더군요..

그래서 이렇게 강조를 했습니다.

 

 

- 에셋번들 사용하기 세 번째 - 에셋 다운로드 받기 (웹, 서버) -

 

이전 시간에 로컬에서 에셋번들을 다운로드 받는 방법을 알아 보았지요?

 

이제 두둥..서버 입니다. 혹은 웹이지요 ㅎㅎ

 

 

일단 "서버"에서 받으려면 "서버"가 필요하겠지요?..그럼 제가 서버를 가지고 있을까요? 

 

하나 구성해서 이것저것 해보고 싶은 생각이 굴뚝 같지만..저도 서버가 없습니다..

 

 

어떻게 할까 하다가...방법이 없을까...해보다가 아! 드롭박스..

(Dropbox - 많이들 쓰시지요?, 물론 드롭박스를 안쓰시는 분들은..계정하나 만드셔야 합니다. 어렵지 않습니다)

 

드롭박스에 public 에 올려놓고 공개링크를 걸어놓으면 다운로드 받을수가 있지...하는 생각에 시도를 해보았는데! 되더군요..

 

그래서 웹에서, 서버에서 받는 방법을 사용할 수가 있습니다 하하 서버없이도! 좋지요?

(물론 서버를 구성하실 수 있는 분들이나 이미 가지고 계신 분들은 그걸 이용하시면 됩니다.)

 

이번에는 이미지를 하나 에셋 번들로 만들고, 이걸 드롭박스에 올려놓고 링크를 복사해서 불러와서 화면에 띄우는 데 까지 할겁니다.

 

복잡해 보이지만 우리는 이미 우리가 알아야할 모든 걸 다 알아버렸습니다. 물론 드롭박스 사용법은 간단히 보여드리겠습니다.

 

먼저 이미지를 하나 받습니다.

UnityLogo.jpg

저는 요걸 사용하겠습니다.

 

똑같이 에셋 번들을 만듭니다. 


1.png

프로젝트 폴더에 Resources -> Textures 폴더를 만들고 UnityLogo.jpg 파일을 선택한 상태에서 메뉴의 Assets -> Build....을 클릭해서 에셋 번들을 만듭니다.

 

이번에도 저는 Resources - > Assetbundles -> Image.unity3d 라는 이름으로 에셋번들을 저장했습니다.


2.png

보이시지요?

 

이제 드롭박스에 올려보겠습니다.


3.png

www.dropbox.com 에 접속하시면 이런 화면이 나옵니다.

 

가입이 안되어 있으신 분들은 가입을하시고 사용하시면 됩니다.

 

저는 계정이 있으니 로그인을 해보겠습니다.

 

로그인을 하고 좀 전에 에셋번들을 올려보겠습니다.


4.png

드롭박스에 접속을 하고 Public 폴더에 들어가서 Image.unity3d 에셋 번들은 업로드 합니다. 위에 보이죠?

 

이제 마우스 오른쪽 버튼을 누르고 공개 링크 복사를 합니다.


5.png

위의 링크를 복사해서 이제 코드에 붙여 넣습니다.

 


6.png

코드를 설명해 드리겠습니다.

 

일단 path 변수에 위에서 복사한 공개링크 값을 붙여 넣었습니다.

 

여기서 지난번과 차이가 나타납니다. 

여기서는 "file://" 이걸 앞에 붙여주지 않습니다 !! 

그냥 http:// ~~~ 이렇게 "File://" 이 앞에 붙어있지 않으면 유니티는 아! 이건 그냥 웹에서 받아오면 되는구나! 하고 알아먹습니다.

 

그리고 지난 번과 똑같이 GUI 버튼을 하나 만들어서 그 버튼을 누르면 LoadAsset() 함수를 부르도록 했습니다.

 

LoadAsset함수는 WWW 변수를 이용해서 다운로드를 시도하고,

첫번째 if문은 error를 확인하기 위함입니다. 

 

오류가 없으면 else 로 가겠죠?

 

일단 에셋 번들이 이미지로 부터 만들어 졌기 때문에 이미지를 띄우려면 오브젝트가 하나 필요합니다.

 

저는 Plane 오브젝트를 생성했습니다. 

(else 다음에 보이는 첫줄이 Plane 오브젝트를 동적으로 생성하는 부분입니다. 

GameObject.CreatePrimitive(PrimitiveType. 까지 찍어보시면 Plane말고 다른것도 생성 가능하도록 되어 있습니다. 궁금하신 분들은 한번 해보세요 ㅎㅎ)

 

Plane 오브젝트는 기본적으로 면이 위를 향하고 있습니다. 근데 저는 면이 화면을 바라보도록 하고 싶어서 그 아래줄에 rotation 값을 바꿨습니다.

(new Vector3(90f, 180f, 0f) <-- Vector3 안에 값들은 인스펙터의 rotation 에 입력하는 값과 같습니다.)

 

그리고 Plane 오브젝트가 갖는 재질(Material)의 텍스쳐를 다운로드 받은 에셋번들로 바꿔 줬습니다.

 

결과 화면을 보겠습니다.


7.png

보이죠 ? 잘 뜹니다. 

 

한가지를 빼먹었네요..다운로드가 안되는 분들도 있으실 겁니다.

 

에러가 ~~뭐시기 crossdomain.xml polcity ~~ 이런 오류가 아마 나온텐데.

 

다운로드 받을 웹의 경로를 유니티에 알려줘야 합니다.

 

해보겠습니다. 

 

에디터 메뉴에서 Edit -> Project Settings -> Editor 를 누르고 


8.png

인스펙터 뷰에 http://dl.dropbox.com를 복사해 줍니다 아래와 같이요.

 


9.png

이렇게 하고 실행을 하면 제대로 불러와서 아래와 같은 결과가 화면에 나타나게 됩니다.

 


7.png


에셋 번들을 만들고 웹에서 부르는 것까지 해봤는데, 로컬에서 부르나 웹에서 불러오나 크게 다르지 않죠?

 

로컬에서 부르는 경우에는 주소값 앞에 "file://"을 붙여주기만 하면 되고, 웹인경우, "http://"로 시작하면 됩니다.

(앞에 붙는 값은 대소문자를 가리지 않습니다. "File://" 도 좋고 "file://" 도 좋습니다.)

 

어렵지 않죠?

 

다음에는 WWW.LoadFromCacheOrDownload 함수를 이용해서 버젼관리 하는 법에 대해 알아보겠습니다.

 

그리고 그 다음에는 모바일에서 불러오는 것도 해보지요. 

 

모바일에서도 로컬 / 웹 둘다 해볼껀데, 미리 말씀드리자면, 로컬이 조금 더 까다롭습니다 모바일의 경우에는..

(아 그리고...제가 iOS 개발자 등록을 하지 않은 상태라 아쉽지만 iOS 쪽으로 빌드를 해서 테스트가 불가능합니다..안드로이드로 진행하겠습니다.)

 

그리고 이 시점에서 하나 또 언급을 해드려야 겠습니다. 이거 중요합니다!!

 

http://korea.unity3d.com/board/index.php?db=knowhow&no=2196&mari_mode=view%40view&cate=&page=1&listURL=http%3A%2F%2Fkorea.unity3d.com%2Fboard%2F%3Fdb%3Dknowhow&search=&search_str=&temp=

위 링크에 가보시면 에셋 번들 전체에 대해서 정리가 잘 되어 있는데요.

 

중간에 "에셋 번들은 서로 호환 되나요?" 질문에 대한 설명에 보면 서로 호환이 안되는 것을 알 수 있습니다.

 

그래서 안드로이드에서 사용하는 에셋 번들 따로, iOS에서 사용하는 것 따로 이런식으로 에셋 번들을 따로 만들어 주어야 합니다.


10.png

위에 보시다 싶이 호환이 서로 되는 게 있고 안되는 게 있습니다.

 

참고하시고 에셋 번들을 제작해야 합니다.

 

요 부분에서는 에셋 번들을 제작하는 코드를 아주 사~알 짝 수정해 줘야 합니다.

 

모바일 에셋 번들 다룰 때 설명 드리지요.

 

감사합니다.

 

 

- 에셋 번들 사용하기 네 번째 - 에셋 다운로드 받기 - 버젼 관리 -

 

이번에는 WWW.LoadFromCacheOrDownload 요 함수를 다뤄 보겠습니다.

 

이 함수는 인자를 두개 받는데, 하나는 에셋 번들 경로 입니다. 이 경로는 앞에서 다뤄 봤던데로, 로컬이어도 좋고, 웹이어도 좋습니다.

 

두 번째 인자는 "버젼" 입니다. 다시 말씀 드리면 에셋 번들의 버젼 입니다.

 

그리고 이 버젼은 캐시를 할지 안할지를 비교하는 정보로 사용됩니다.

(인터넷 브라우져의 캐시 기능 아시죠? 유니티에서도 같은 기능을 제공한다고 생각하시면 됩니다. - 전혀 다르지 않습니다.)

 

이 함수를 이용해서 에셋번들을 다운로드하려고 하는 시점에서 바로 다운로드하지 않고, 캐시 폴더에 저장되어 있는 에셋 번들 정보들과 비교해서, 

 

다운로드 받으려고 하는 에셋 번들이 이미 캐시 폴더에 저장되어 있는 에셋 번들이라면 캐시 폴더에서 바로 불러옵니다.

 

그렇지 않고 캐시폴더를 확인 해 봤는데, 지금 다운로드 받으려는 에셋 번들이 새로운 놈이다! 라고 판단되면 새로 다운로드를 받고 캐시 폴더에 저장을 해 둡니다.

 

 

이게 동작원리의 전부입니다.

 

사용방법도 크게 다르지 않습니다. 하지만 몇 가지 주의해야 할 사항이 있습니다. 요 것만 주의하면 우리는 에셋 번들을 마스터 할 수 있어요!!

 

서두가 조금 길었는데 해보겠습니다.

 

 

이 번에는 사용할 이미지가 두개 입니다.

 

한 가지는 지난번과 같고 요놈 - 저는 이 이미지를 버젼 1로 놓고 진행을 해보겠습니다.


UnityLogo_origin.jpg

또 한가지는 요놈 - 이 이미지는 버젼 2로 해서 진행을 하겠습니다.

logo.jpg

네 이렇게 하면 구분이 되겠지요 ?

 

이미지가 준비 되었으니 진행을 해봐야죠?

 

먼저 위의 이미지를 에셋 번들로 만들고 드롭박스에 올려두겠습니다.


1.png

에셋 번들을 만듭니다. 저는 UnityLogo.unity3d 라는 이름으로 만들었습니다.

 

이걸 드롭박스에 올려두고 다운로드를 진행해 보겠습니다. 참고로 지금 만든 에셋 번들은 버젼이 1로 정했습니다.


2.png

공개 링크를 복사해서 다운로드할 준비를 하고, 소스코드를 작성을 합니다.

 


3.png

이게 코드의 전부입니다. 일단 path에 위에서 복사한 공개링크값을 넣어주고, 

 

GUI버튼을 하나 만들어서, 버튼이 눌리면 LoadAsset 함수가 실행되도록 했습니다.

 

이 함수를 들어다 보면, 맨 처음에 while 문에서 Caching.ready 가 true인지 확인합니다.

 

false 면 while문에서 계속 기다리게 하고 있죠.

 

!!! 이거 매우 중요합니다 !!!

 

WWW.LoadFromCacheOrDownload 함수를 사용할 때 유니티는 바로 다운로드 하지 않고, 캐시 폴더에 저장이 되어 있는지 확인해 본 다음에 

 

없으면 다운로드 한다고 설명 드렸습니다.

 

그런데, 유니티는 이전에도 언급해드렸듯이 이 정보들을 항상 쥐고 있지 않습니다. 

 

Caching.ready는 캐시 폴더에 저장되어 있는 에셋 번들 정보들을 모으는 시간을 주기 위해서 확인 해줘야 합니다.

 

그리 오랜 시간이 걸리지는 않지만, Caching.ready를 확인 하지 않고, 바로 WWW.LoadFromCacheOrDownload를 사용하면, 정보를 다 모으기 전에

 

동작을 하는 경우가 발생할 수 있습니다. 이런 경우에는, 가지고 있는 에셋 번들 임에도 다운로드를 받거나 또는 다운로드를 받아야 하는 에셋 번들임에도

 

엉뚱한 에셋번들을 캐시에서 불러오거나, 아니면 못 불러오는 경우도 발생할 수 있습니다.

 

물론...정상동작을 할 때도 많이 있습니다. 하지만 이건 잘못된 경우입니다. 

 

 

네 그래서 유니티에게 충분한 시간을 주고 WWW.LoadFromCacheOrDownload를 사용해서 부릅니다. 위에 보시면 값으로 받아온 path 경로와 버젼은 1로 

 

주고 실행하도록 했습니다. 결과 화면을 보지요.

(using 문은 구글님께 물어보시면 자세히 나와있으니 참고를 하시고, 함수 끝부분에 .Unload를 추가해서 에셋 번들을 로드하고 안의 에셋으로 게임 오브젝트를

생성한 다음에 다쓴 에셋 번들을 해제 하도록 했습니다.)


4.png

네 익숙한 화면이지요? ㅎㅎ

 

로딩이 잘 되었네요.

 

이번에는 두 번째 이미지로 에셋 번들을 만들어 보겠습니다.

 

저는 같은 상황에 쓰이는 에셋 번들인데, 조금 수정을 했다..라는 의미로 두 번째 이미지를 사용할 거에요.

 

그러니까, 게임또는 앱을 런칭하고, 업데이트를 하는 상황이라고 가정하면, 지금 보이는 저 로고를 업데이트 버젼에서는 다른걸로 수정을 한거죠.

 

 

여기에서 주의하실 점은 두 번째 에셋번들을 만들 때 첫번째 만들었던 에셋 번들과 같은 이름으로 만들어 주어야 한다는 점입니다.

 

유니티가 WWW.LoadFromCacheOrDownload 함수를 써서 다운로드하기 전에 캐시 폴더 정보와 비교할 때는 "파일 이름 - 에셋 번들 이름" 과 "버젼" 이 두가지를

 

비교합니다. 따라서, 같은 파일이름인데, 버젼을 다르게 해서 불러오게 하고 싶은겁니다.

 

해보도록 하겠습니다. 일단 에셋 번들을 만듭니다.


5.png

이번에도 UnityLogo.unity3d 라고 같은 이름으로 만들었습니다.

 

드롭박스에 똑같이 올려놓고, 다운로드를 받게 해보겠습니다.


6.png


 

 

 

 

 

아까와 같이 공개링크를 복사하고 소스코드 path 값에 이 값을 넣도록 하겠습니다.

 

그리고 다른 부분은 건드리지 않고 바로 실행을 해보겠습니다.


7.png

짜잔~ 어;; 아까와 결과가 같습니다..분명히 다른 이미지로 에셋 번들을 만들고, 경로도 바꿔서 불러왔음에도 이미지가 바뀌지 않았습니다.

 

어떻게 된 걸까요?..하하..눈치 빠르신 분들은 알겠지만, 이미지를 다르게 했지만, 에셋 번들 이름이 같고, 소스코드에 넣어준 "버젼"도 아까와 동일하기 때문에,

 

유니티는 아..이거 아까 받아 놓은 거네!! 라고 생각해서, 캐시 폴더에 아까 받아 두었던, 에셋 번들을 로딩하게 됩니다.

 

그래서 아무런 변화가 없었습니다.

 

그럼 소스코드의 "버젼"을 다르게 주겠습니다. 아까 1로 했으니 2로 해보지요.


8.png

버젼을 바꿨습니다. 빨간색 박스 부분을 보시면 2로 바꿨죠?

 

이제 실행해 보겠습니다.

9.png

짜잔!! 이제 정말 우리가 원하는 이미지가 나왔습니다.. 하하

 

좀 더 확실한 이해를 돕기 위해서 버젼을 다시 1로 해서 불러보겠습니다.

 

결과는 예상하셨던 대로 아래와 같이 1번 이미지가 로드됩니다.


10.png

절대 아까 이미지 다시 쓴거 아닙니다...버젼을 1로 고치로 다시 로드를 해왔습니다.

 

지금의 경우는 유니티가 또 정보들을 비교해보고, 아 이거 내가 가지고 있는 거네, 캐시 폴더에서 부르면 되겠네! 라고 생각해서, 

 

아까 저장되었던 이미지를 로딩한 거에요.

 

 

다시 버젼을 2로 바꿔서 로딩을 하면, 네...예상과 같이 두번째 이미지를 로딩하지요??


11.png

어떠신지요? 어렵지 않지요?

 

 WWW.LoadFromCacheOrDownload 함수를 사용할 때 이 함수를 호출하기 전에 반드시 Cacheing.ready 가 true가 될때까지 기다렸다가 호출해야한다는 점을

 

명심하시면, 어렵지 않게 사용하실 수 있습니다.

 

 

사실...방금 말씀 드린 이부분을 설명드리려고, 이리 장황하게 예제를 준비했더랬습니다...많은 분들이  WWW.LoadFromCacheOrDownload의 사용방법과

 

동작 원리는 잘 아시는데, Caching.ready를 해줘야한다는 점을 모르시는 분들이 많더라구요.

 

 

저는 어디까지나 사용방법만 알려드렸는데, 다양한 방법으로 응용 및 사용이 가능하겠지요?

 

어떤 모델링이나 이미지를 패치할때, 같은 상황에 쓰이던 이미지가 바뀌었을 때, 이런경우 원래는 씬을 바꿔서...다시 빌드를 해야할 수도 있지만, 

 

설계를 잘해서 에셋 번들로 관리하고 에셋 번들의 "버젼"관리도 잘 하게 되면, 그냥 에셋 번들을 다시 로딩하게 해서 간단히 처리할 수 있습니다.

 

 

쉽게 설명하려고 최대한 풀어서 설명하려고 노력했고, 쉬운 예제를 들고 왔지만 제가 원래 좀 부족한 놈이기에...전달이 잘 안될 수 있습니다.

 

질문 및 코멘트 감사히 생각할 테니 어떤 의견도 좋으니 보시고 댓글 하나 달아주셔요..

 

 

다음에는 안드로이드에서 에셋 번들 불러오는 걸 한번 해보겠습니다.

 

뭐...전혀 다르지 않습니다. 단지...장치에서 실행을 한다는 거 외에는 ㅎㅎ

 

 

감사합니다.

 

 

- 에셋 번들 사용하기 다섯 번째 - 웹에서 모바일(안드로이드)로 다운로드 -

 

지금까지는 에디터에서만 실행을 하고 결과를 확인했지요.

 

드디어, 모바일에서 결과를 확인해보려고 합니다. 

 

모바일 장치에서 사용한다고 해서 크게 달라지는 점은 없습니다만...

 

주의하실 점은 있습니다.

 

에셋 번들을 제작하실 때 이전에 언급했듯이 서로 호환이 안됩니다.


1.png


 

표에 나와 있듯이, 모바일로 에셋 번들을 사용하려는 경우에는 따로 제작을 해줘야 합니다.

 

그럼 여기서 고민이 생깁니다...모바일용 에셋 번들은 따로 만들어 줘야 하는 건 알겠는데, 뭘 어떻게 만들어야 하나...

 

 

네...첫 번째 시간에 했던 에셋 번들 생성하기 기억하시나요? ㅎㅎ 안보신 분들은...보고 오시면 좋겠네요.

 

에셋 번들 생성 코드를 아주 살짝 손을 볼거에요. 그렇다고 장황하게, 고칠 필요는 없습니다. ㅎㅎ

 

이전 코드를 살펴보겠습니다.


2.png


 

빨간 네모로 표시한 부분을 아주 살짝 수정할 거에요. BuildPipeline.BuildAssetBundle() 함수는 여러 형태로 오버로딩 되어 있는데요.

 

위에 나온 코드는 빌드 타겟을 설정해주지 않은 형태 입니다.

 

여기에 빌드 타겟만 안드로이드 / iOS로 해주면 됩니다.

 

해보겠습니다.


3.png


 

빨간 네모와 파란 네모의 차이점이 보이시나요?   

 

BuildPipeline.BuildAssetBundle() 함수의 인자를 넣는 부분 마지막에 보시면, "BuildTarget.Android" 가 추가 되었습니다.

 

네 이것 말고는 차이점이 없습니다.

( [Menuitem()] attribute가 어떻게 사용되는지 궁금하신 분들은 구글님께 검색을...)

 

이렇게 하고, 에디터 메뉴로 가서 Assets 를 눌러보면 아래에 "Build AssetBundle from Selection - To Android" 항목이 추가된 걸 확인하 실 수 있습니다.


4.png


 

보이시지요? 네...그 뒤의 과정은 이전에 했던 과정과 똑같습니다.

 

물론...로컬에서 불러오는 경우는 좀 다릅니다. 이건 나중에 설명 드리겠습니다.

 

일단, 웹에서 불러오는 걸 해보겠습니다.


5.png


 

이전 시간에 했던 캐싱을 이용하는 방법으로 한번 해보겠습니다. 검정색 바탕의 이미지를 버젼1 으로 먼저 만들고 다운로드 진행하겠습니다.

 

저는 Android.unity3d 라는 이름으로 에셋 번들을 생성 했습니다.


6.png


드롭박스에 올리고

 


7.png


 

불러오는 코드를 보겠습니다.

 

이전시간에 썼던 코드 그대로 입니다. 다시 버젼을 1로 하고 안드로이드 장치에서 실행을 해보겠습니다.


8.png


 

제 휴대폰에서 스크린샷 찍은 사진 입니다.

 

그대로 잘 불러왔지요? ㅎㅎ

 

다른 이미지를 같은 이름의 에셋 번들로 생성하고 버젼을 다르게 해서 불러 보겠습니다.

 

과정은 이전 시간에 했던 것과 동일 하니까, 혹시 모르시겠다 하신 분들은 이전에 올려놓은걸 확인하세요. 아래 링크 있습니다.

http://www.unitystudy.net/bbs/board.php?bo_table=writings&wr_id=155


9.png


 

동작을 잘 하죠 ? ㅎㅎ

 

모바일에서 에셋 번들을 사용한다고 해서 달라지는 부분은 크게 없습니다.

 

처음에 했던 에셋 번들 생성하는 부분만 조심하면 됩니다.

 

모바일에서 에셋 번들 사용하는 것도 어렵지 않죠 ? ㅎㅎ

 

다음엔 안드로이드 로컬에서 에셋 번들을 불러오는 부분을 진행해 보겠습니다.

 

 

 

사실 이부분은 실제 개발할때 어떤 부분에서 사용을 하는지 정확히 모르겠습니다. apk 사이즈를 줄이는 방법 중 하나로 많은 부분을 에셋 번들로 만들고, 

 

서버에 저장해서 지금 보신대로 캐싱을 이용해서 관리하는 걸로 알고 있습니다. 

 

이렇게 하면 에셋 번들을 다운로드 한 상태라면 (처음에는 다운로드 해야겠지요) 그 다음부터는 로컬에서 부르는 것과 다르지 않기 때문에, 

 

이 방법을 많이 사용하는 걸로 알고 있습니다만...그래도 궁금하니까 진행해 보겠습니다.

 

읽어주셔서 감사해요 ㅎㅎ

 

 

 

 

- 에셋 번들 사용하기 여섯 번째 - 로컬에서 모바일(안드로이드)로 다운로드 -

 

이번에는 지난 번에 이어서 안드로이드에서 에셋 번들 사용하는 방법을 이어서 진행합니다.

 

웹에서 에셋 번들을 다운로드 하는 것 까지 진행을 했었는데요.

 

딱히 다른점은 없었지요? ㅎㅎ

 

이번에는 안드로이드 로컬에서 에셋 번들을 다운로드(불러오는) 방법에 대해서 알아 보겠습니다.

(사실 에셋 번들이라고 해서, 난 에셋 번들 안쓸꺼야...하시는 분들이 계실지도 모르겠지만, 에셋 번들이 아닌 파일로 로드는 가능합니다 같은방식으로..)

 

안드로이드 로컬에서 불러오는 방법 / 위치는 두 가지 정도 생각할 수 있겠습니다.

 

하나는 실제 프로젝트 폴더 내부입니다.

 

유니티가 안드로이드 apk로 빌드가 되면, 이 프로젝트 폴더가 가공되고 압축이 되어서 apk로 만들어 집니다.

 

그러면 프로젝트 뷰 어딘가에 에셋 번들을 넣어 둔 상태라면 apk 어딘가에 해당 폴더가 있겠지요? 

 

사실 우리는...에셋 번들을 위해서는 선택권이 없습니다.."StreamingAssets" 라는 이름의 폴더를 만들어서 여기에 두면, 나중에 압축이 되어 

 

안드로이드 프로젝트의 Asset 폴더로 들어갑니다. 

 

내부에서 어떤 동작이 일어나서 StreamingAssets 라는 폴더가..Asset 으로 바뀌는 지 까지는 확인을 못했습니다.

(아직 내공이 부족해서...)

 

그런데 유니티에서는 안드로이드 프로젝트에 있는 Asset 폴더에 파일을 넣어두기 위해서는 "StreamingAssets"라는 폴더를 만들어 두어야 합니다.

 

말로만 설명하니까 무슨 말인지 모르겠죠? 보여드리겠습니다.


1.png


 

현재 저의 유니티 프로젝트 뷰 모습입니다. StreamingAssets 폴더에 현재 두 개의 에셋번들이 있습니다.

 

보이시지요?  이제 apk로 빌드를 해서 한번 까(?) 보겠습니다.


2.png


빌드를 한 후에 생성된 .apk 파일을 .zip 파일로 확장자를 바꿔 줍니다.

 


3.png


그리고 압축 해제 프로그램 (winrar 나 알집 같은..) 으로 열어 봅니다.

 


4.png


 

압축 해제 프로그램으로 열어봤습니다. 위에 assets라는 폴더가 보이시죠?

 

assets 폴더에 뭐가 있는지 확인해 보겠습니다.


5.png


 

보이시죠? StreamingAssets 폴더에 넣어두면 apk로 만들어 지고 나서 assets 라는 폴더 안에 들어가게 됩니다.

 

사실이 assets 폴더는 이클립스로 안드로이드 앱을 개발해 보신 분들이라면 아시겠지만, 안드로이드 프로젝트의 assets 폴더 입니다.

 

유니티에서 StreamingAssets라는 이름의 폴더를 만들고 여기에 에셋 번들을 넣어두면 이렇게 빌드가 되고 나서 assets 폴더에 들어가게 되는 것이죠.

(Resources 폴더의 내용은..bin 폴더 안에 들어갑니다. 그 외 다른 내용들이 있지만 여기에서 다루지는 않겠습니다.)

 

이제 확인이 되셨죠? 

 

왜 StreamingAssets라는 폴더에다가 에셋 번들을 넣어 두어야 하는지에 대한 이유를 방금 눈으로 보셨습니다.

 

===============================================================================

assets 폴더가 아닌 다른 위치가 넣고 불러오고 싶다...하시는 분들은...assets 폴더 안에 넣어두고 관리하는 편이 정신 건강에 좋다는 말씀을 드립니다...

 

물론 어디까지나, 유니티 프로젝트 폴더 안에서 접근할 때의 얘기 입니다. 

 

유니티 프로젝트가 아닌 안드로이드 장치의 다른 경로에서 불러오는 경우라면 또 얘기가 달라집니다.

===============================================================================

 

내 이렇게 StreamingAssets 폴더를 만들고 거기 안에 에셋 번들을 넣어 두면 준비는 모두 끝났습니다.

 

이제 에셋을 불러오는 코드를 살펴 보겠습니다.

 

여기에서 주의할 점이 있습니다 바로 경로 설정 값이 조금 다릅니다...

 

http://docs.unity3d.com/Documentation/Manual/StreamingAssets.html

 

위 링크에 가시면 StreamingAssets에 접근하는 경로 값이 나와 있습니다.

 

중간에 보시면 안드로이드의 경우에 path = "jar:file://" + Application.dataPath + "!/assets/"; 이렇게 나와 있습니다. 

 

맨뒤에 "!/assets/Android.unity3d"; 이렇게 에셋 번들이름을 추가해 주면 불러올 수 있습니다.


6.png


 

코드를 살펴 보겠습니다. 다른 부분은 다른게 없습니다. 여러분이 조심하셔야 할 점은 바로 path 변수에 넣어줄 값입니다.

 

위에서 설명한대로 넣었죠? 이제 안드로이드 장치에 넣어서 결과를 확인해 보겠습니다.

(에디터에서 실행하면...불러오지 않습니다. 장치에서 불러야 제대로 실행이 됩니다.)


7.png


 

잘 불러왔습니다. 하하..


몇 가지만 알고 계시면, 프로젝트 뷰 안에 있는 에셋 번들을 로딩하는 것도 어렵지 않습니다. 


유니티 프로젝트에서 StreamingAssets 라는 폴더를 만들고 그 안에다가 에셋 번들을 넣어두고 


불러오는 path 경로를 "jar:file://" + Application.dataPath + "!/assets/Android.unity3d"; 이런식으로 넣어주면 


로컬에서도 쉽게 에셋 번들을 불러올 수 있습니다.



프로젝트 폴더 내부에서 부르는 방법하고, 안드로이드 장치 다른 위치에서 부르는 방법을 이번 강좌에 다 적으려고 했건만...


쓰다보니 너무 길어졌네요.


=======================================================================================

사실 이 방법은 실용적(?)인지는 잘 모르겠습니다.

모바일 장치에서 에셋 번들을 만드는 이유는 효율적 에셋 관리 측면도 있겠지만 사실 apk 사이즈를 줄어려는 목적이 가장 큰 걸로 알고 있습니다.

그런데 지금같은 방법으로 하면 apk사이즈는 줄어들지 않지요...

 

하지만 궁금증 해소라는 측면에서는 충분한 가치가 있겠죠? 또..제가 생각하지 못하는 부분 또는 상황에서 충분히 이용할 수도 있다고 생각합니다.

=======================================================================================


다음에 이어서 안드로이드 장치 다른 위치 예를 들면 Document 폴더나 Download 폴더에서 불러오는 방법에 대해서 알아보겠습니다.


사실 이 부분을 원하시는 분들이 더 많을 것 같네요.


감사합니다.

 

 

 

- 에셋 번들 사용하기 일곱 번째 - 안드로이드 장치에서 에셋 번들 다운로드 -

 

지난 시간에 이어서 안드로이드 장치 로컬에서 에셋 번들을 불러오는 부분을 이어가겠습니다.

 

이전에 진행했던 부분을 간략히 정리를 해보겠습니다.

 

유니티 프로젝트가 apk 로 빌드가 될때 StreamingAssets 폴더가 안드로이드 프로젝트의 asset 폴더로 변환이되고 

 

이 폴더에 접근을 해서 불러오는 방법을 알아보았습니다.

 

 

이번에는 다른 위치에서 불러오는 방법을 진행해 보지요.

 

예를 들면 에셋 번들이 Document 나 Download 같은 안드로이드 장치 내부이지만 

 

apk가 설치되어 있는 프로젝트 폴더 내부가 아닌 위치에 있는 경우에 불러오는 방법을 진행해 보겠습니다.

 

 

거창하게 설명을 했지만, 사실 우리는 이제 알건 다 알아버렸습니다.

 

달리 설정해줘야 하는 부분도 없습니다. 불러오는 경로만 조심해서 불러오시면 됩니다.

 

AndroidLocal.unity3d 라는 이름으로 에셋 번들을 생성해서 Download 폴더 밑에 두고 진행을 하겠습니다.

 

1.png


 

폴더 이름을 보시면 Download라고 되어 있지요? 안드로이드 장치 내부의 Download 폴더 안에 방금 생성한 

에셋 번들을 복사해서 넣어 두었습니다.

 

이제 로드를 해보겠습니다.

2.png


 

위의 코드를 보면 다른 부분은 지금까지 진행했던 코드에서 달라진게 없습니다. 단지 path 값만 바뀌였지요..

 

"file://" + "/sdcard/download/AndroidLocal.unity3d" 이런식으로 값을 주시면 됩니다. 로컬에서 부르기 때문에 앞에 "file://" 을 붙여주고,

 

/sdcard/ 라고 시작해서 접근을 하면 안드로이드의 다른 폴더에 접근이 가능합니다.

 

저도 예전에 안드로이드 장치 내부의 다른 부분에 접근하는 방법을 알아보다가 애를 먹었던 기억이 있는데...사실 엄청 쉽습니다.

 

path 값만 제대로 주면 잘 불러오게 되어 있습니다.

 

결과 화면을 보겠습니다.


3.png


익숙한 이미지를 하나 불러봤고, 노파심에 다른이미지를 에셋 번들로 만들어서 한번 더 실행을 해봤습니다.

 

4.png


 

이것도 잘 되네요..이미지를 다운로드 받고 설정을 제대로 안해줘서...로딩화면이 이쁘지는 않지만 에셋 번들은 잘 불러 왔습니다.

 

 

지금까지 에셋 번들을 사용하는 방법을 알아 보았습니다. 에디터에서 실행을 해보았고, 모바일에서도 진행을 했습니다.

 

웹에서 받는 방법 로컬에서 부르는 방법, 안드로이드에서도 로컬 / 웹에서 불러오는 방법을 알아보았습니다.

 

 

에셋 번들 사용하실 때 주의할 점을 간략하게 정리를 해보겠습니다.

 

앞에서 알아 봤듯이 각 플랫폼별로 서로 호환이 안되는 부분도 있기 때문에, 에셋 번들 생성할 때 한번 주의를 하시고, 

 

상황별로 path 경로 값을 넣는 부분만 주의를 하시면 에셋 번들 사용하는 데 어려움이 없으실 거에요.

 

 

이걸로 에셋 번들 시리즈를 마치려고 합니다.

 

처음부터 다 읽어 주시고, 같이 예제를 진행해 주신 분들 너무 감사합니다.

 

여러분이 에셋 번들을 사용하시고 이해하는 데 조금이나마 도움이 되었으면 하는 바램입니다.

 

 

조만간에, 다른 주제로 또 강좌를 올려보도록 하겠습니다.

 

감사합니다.

 

--------------------------------------------------------

http://unitystudy.net/bbs/board.php?bo_table=newwriting&wr_id=152&page=3

 

http://icoder.tistory.com/entry/Unity3D-강좌-애셋번들-개념정리

http://blog.daum.net/kb082/11745799

http://botta.tistory.com/38

http://www.devbb.net/viewtopic.php?f=37&t=1167

http://abcprodev.tistory.com/143

http://lazli.tistory.com/63

https://developer.vuforia.com/forum/unity-3-extension-technical-discussion/object-not-augmenting-correctly-ios-works-android

http://answers.unity3d.com/questions/55512/Does-AssetBundle-on-Android-work-well-.html

http://dkdlel072.tistory.com/191

http://clack.tistory.com/107

http://devkorea.co.kr/bbs/board.php?bo_table=m03_qna&wr_id=28676



출처: http://theemeraldtablet.tistory.com/entry/AssetBundle-만들기-Unity-Pro-iPhone-Advanced-Only [에메랄드 타블릿]


반응형
Posted by blueasa
, |


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

반응형
Posted by blueasa
, |

에셋번들을 만들기 위해서는 먼저 에셋번들로 만들 리소스를 분류해야한다.

프로젝트 뷰에서 리소스를 클릭하면 아래와 같은 뷰를 볼 수 있는데

이 뷰의 하단에 AssetBundle의 리스트 박스가 있다.

디폴트는 None 으로 되어있는데 New를 사용하여 새로운 에셋번들 이름을 정할 수 있다.

에셋번들로 사용할 리소스의 에셋번들 이름을 설정해 준다.




헌데 에셋번들 이름만 설정하는 것으로는 에셋번들이 자동적으로 생성되지 않는다.

때문에 직접 코드를 작성해야한다.

1
2
3
4
5
6
7
8
9
10
11
12
using UnityEngine;
using UnityEditor;
 
public class AssetBundleBuilder : Editor 
{
    [MenuItem("Assets/BuildBundle")]
    static void BuildBundle()
    {
        BuildPipeline.BuildAssetBundles("AssetBundles");
    }
}
 
cs

MenuItem을 사용하여 유니티의 Asset탭에 BuildBundle탭이 나타나게 하였다.

BuildPipeline을 이용하여 에셋번들의 이름이 지정된 리소스를 에셋번들로 만들어준다.

BuildAssetBundles는 에셋번들을 만들어주는 함수로 매개변수에는 에셋번들이 저장될 경로를 넣어주었다.


Asset탭에 BuildBundle탭이 생겼다.


사용해보면 에셋번들이 생성된다.




요롷게 지정해줬던 폴더에 Bundle 파일이 생성되었다. 

(아, 폴더가 미리 만들어져있지 않으면 에러를 뱉어냅니다. 폴더의 경로는 프로젝트 폴더 기준)



이제 이 에셋번들을 다운로드 할겁니다.

인터넷에 올려서 다운로드 할 수도 있지만 저는 로컬에서 받는 방식으로 해볼 것입니다.

저의 로컬 디렉터리 URL은 file:///C:/Users/UnderCode/Desktop/1.UnityTest/AssetBundleTest/bundle  입니다.

이 경로를 통해 bundle 파일을 받게 됩니다.


에셋번들을 다운로드 받는 방법은 크게 두가지가 있습니다.

WWW 클래스를 이용해서 그냥 다운 받거나

WWW.LoadFromCacheOrDownload() 함수를 이용하여 받는 방법입니다.

LoadFromCacheOrDownload() 함수는 에셋번들을 인터넷으로 부터 캐싱하여 받는 방식으로 cache 폴더에 저장됩니다.

다음에 다시 다운받을 때 하드디스크에 에셋번들이 있으면 하드디스크에 있는 에셋번들을 사용하고 없으면

 다운로드 받아서 사용합니다.



먼저 WWW를 이용하여 다운 받는 방법 

(string bundleURL = "file:///C:/Users/UnderCode/Desktop/1.UnityTest/AssetBundleTest/bundle";)

(AssetBundle bundle;)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    //방법 1
    IEnumerator AssetBundleLoad()
    {
        WWW www = new WWW(bundleURL);
 
        yield return www;
 
        bundle = www.assetBundle;
 
        AssetBundleRequest abr = bundle.LoadAssetAsync<Sprite>("TestImage");
        yield return abr;
        assetImage.sprite = (Sprite)abr.asset;
 
        abr = bundle.LoadAssetAsync<TextAsset>("AssetBundleTest");
        yield return abr;
        TextAsset textAsset = (TextAsset)abr.asset;
        assetText.text = textAsset.text;
 
        abr = bundle.LoadAssetAsync<AudioClip>("MazeMusic");
        yield return abr;
        assetAudio.clip = (AudioClip)abr.asset;
        assetAudio.Play();
 
        bundle.Unload(false);
    }
cs



LoadCacheOrDownload() 함수를 이용하는 방법

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    //방법 2
    IEnumerator AssetBundleLoad()
    {
        while (!Caching.ready)
            yield return null;
 
        WWW www = WWW.LoadFromCacheOrDownload(bundleURL, bundleVer);
 
        yield return www;
 
        bundle = www.assetBundle;
 
        AssetBundleRequest abr = bundle.LoadAssetAsync<Sprite>("TestImage");
        yield return abr;
        assetImage.sprite = (Sprite)abr.asset;
 
        abr = bundle.LoadAssetAsync<TextAsset>("AssetBundleTest");
        yield return abr;
        TextAsset textAsset = (TextAsset)abr.asset;
        assetText.text = textAsset.text;
 
        abr = bundle.LoadAssetAsync<AudioClip>("MazeMusic");
        yield return abr;
        assetAudio.clip = (AudioClip)abr.asset;
        assetAudio.Play();
 
        bundle.Unload(false);
    }
cs



에셋번들에서 리소를 가져올 때는 리소스의 이름을 사용하여 리소스를 찾고(확장자는 필요 없음)

AssetBundleRequest를 이용하여 받습니다.



출처: http://undercode.tistory.com/21 [UnderCode]

반응형
Posted by blueasa
, |