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

카테고리

분류 전체보기 (2737)
Unity3D (817)
Programming (474)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (228)
협업 (58)
3DS Max (3)
Game (12)
Utility (136)
Etc (96)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (53)
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
04-23 18:01

프로그래밍을 할 때 있어서 모든 일이 처음 설계한대로 흘러간다면 얼마나 좋을까? 하지만 Hello World를 출럭하는 프로그램이 아닌 이상에야 그런 일은 있을 수 없다.


확장성을 위해, 재사용성을 위해, 더 나은 구조를 위해 코드와 설계는 변하기 마련이다. 그 와중에 많은 함수나 변수, 클래스가 추가되고 삭제된다. 물론 혼자서만 하는 작업이라면 코드를 바꾸고 바꾼 코드를 바로 적용하면 되지만, 코드 베이스를 만드는 사람과 그 베이스를 이용해서 작업하는 사람이 따로 있는 상황이라면 이야기가 조금 달라진다. 


만약 베이스를 작업하는 사람이 몇몇의 함수를 삭제하고 다른 이름의 함수로 대체했다면 그 베이스를 응용하는 사람 역시 그에 대한 사실을 알아야 한다. 물론 일반적인 상식으로 베이스 작업자가 베이스를 변경했다면 다른 작업자에게 바로 알려주고 다른 작업자는 바로 변경하는게 맞는 이야기지만, 사람과 사람 사이의 의사소통이라는게 말처럼 쉽기만 하던가. 베이스 작업자가 변경사항을 몇 개는 빠뜨리고 알려줄 수도 있고, 다른 작업자는 이야기를 들었지만 까먹을 수도 있는 일이다. 여튼 의사소통 과정에서 문제가 발생했다면 다른 작업자는 뜬금없이 바뀐 베이스 코드에 당황을 금치 못할 것이다.


그런 상황을 맞이한 다른 작업자는 당연히 문제를 해결하기 위해서 베이스 작업자에게 어떻게 변경된 것인지 물어보던지, 코드를 뒤져서 바뀐 함수를 적용하던지 하는 노력을 하겠지만 아무래도 이런 방식은 해결 속도도 느릴 뿐더러 효율적이지 못하다.


그렇기 때문에 나온 해결책이 바로 [Obsolete] 라는 어트리뷰트이다. 


class TestClass
{
    [Obsolete]
    public void Function1()
    {
    }
}


더 이상 사용하지 않거나 그럴 예정인 클래스나 함수, 변수의 앞에 [Obsolete] 어트리뷰트를 붙여주면 된다. 그렇게 하면 해당 함수를 호출할 때 초록색 밑줄과 함께 더 이상 사용하지 않는 함수라는 경고가 뜬다.



그리고 툴팁에서는 함수 앞에 [deprecated]가 붙게 된다.


이 [Obsolete]는 세 가지 방식의 오버로딩을 지원한다.


class TestClass
{

    [Obsolete]
    public void Function1()
    {

    }

    [Obsolete("Not use anymore.")]
    public void Function2()
    {

    }

    [Obsolete("Not use anymore.", true)]
    public void Function3()
    {

    }
}


[Obsolete] :: 더 이상 사용하지 않는 코드라는 경고만 출력한다.


[Obsolete(string message)] :: 더 이상 사용하지 않는다는 경고에 추가적인 메시지를 남길 수 있다. 이 메시지를 통해 더 이상 사용하지 않는 코드 대신에 사용할 코드를 사용자에게 알릴 수 있다.


[Obsolete(string message, bool error)] :: 추가적인 로그와 함께 이 코드를 사용할 경우에 컴파일 에러를 띄울지를 결정한다. true를 넣어주면 컴파일 에러를 띄워서 이 코드를 사용하면 컴파일을 할 수 없게 된다.



이런 식으로 [Obsolete]를 적절하게 사용하면 베이스 작업자는 코드 작업만으로 다른 작업자에게 코드가 변경되었음을 알림과 동시에 그에 대한 해결책도 전해줄 수 있다. 베이스 작업자가 코드를 변경하고 다른 작업자에게 변경사항을 일일이 알리는 것보다 훨씬 빠르고 효율적인 해결책이다.



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



참조 : http://answers.unity3d.com/questions/559529/how-to-mark-a-method-obsolete.html

반응형

'Programming > C#' 카테고리의 다른 글

ODBC/OleDB로 Excel 읽어서 처리할 때, 255자로 짤리는 경우  (0) 2018.04.13
Dictionary for Loop in C#  (0) 2017.12.05
[펌] string을 byte[] 배열로 변환?  (0) 2017.01.10
[펌] 반복문 유틸  (0) 2016.12.07
[펌] Loop Dictionary  (0) 2016.12.07
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
, |