리소스 로딩하기



유니티IDE의 Assets 안 에는 수 많은 객체를 정의해 둘 수 있습니다. 이미지나 사운드를 비롯하여 스크립트와 프리펩까지 모두 들어갑니다.

이러한 에셋은 컴파일타임에 특정 게임오브젝트와 바인딩되지만 런타임에 스크립트를 통해 사용하려면 리소스를 로딩하는 과정을 거쳐야 합니다.


 

특별한 이름의 폴더top

유니티의 Assets 폴더 밑에는 서브폴더를 자유롭게 만들 수 있지만 미리 지정된 이름으로 폴더를 만들면 특별한 작동을 합니다.

http://wiki.unity3d.com/index.php/Special_Folder_Names_in_your_Assets_Folder

위 목록에서 다양한 특수 폴더명을 볼 수 있습니다. 대소문자는 구별하지 않습니다. 이 중 이번 포스팅에서 눈여겨 볼 폴더명은 Resources 입니다.

Assets/resources

에셋폴더 밑에 일단 resoureces 라는 이름으로 폴더를 만들면 이 폴더를 기준으로 여러 에셋을 런타임에 로딩하여 사용할 수 있게 됩니다.

예를들어 프리펩을 만들어 로딩하고 싶다면 다음과 같은 구조로 서브폴더를 만들어봅시다.

Screenshot_1

resources/block 폴더 안에 들어있는 R1~R5까지의 프리펩을 런타임에 불러오려면 어찌 해야할까요?

 

Resources.Load와 Resources.LoadAlltop

각각의 자원을 부르는 방법은 Resources.Load를 사용하는 것입니다.

GameObject temp1 = Resources.Load( "block/R1" as GameObject;
GameObject temp2 = Resources.Load( "block/R2" as GameObject;
GameObject temp3 = Resources.Load( "block/R3" as GameObject;
GameObject temp4 = Resources.Load( "block/R4" as GameObject;
GameObject temp5 = Resources.Load( "block/R5" as GameObject;

귀찮기 때문에 그저 서브폴더만 넘겨주면 전부 처리해주는 LoadAll을 제공합니다.

object[] temp = Resources.LoadAll( "block" );

LoadAll의 경우는 as GameObject[] 로 형 변환할 수 없습니다. 하지만 개별 요소에 대해서는 형변환할 수 있으므로 큰 문제는 없습니다.

object[] temp = Resources.LoadAll( "block" );
 
forint i = 0 ; i < temp.Length ; i++ ){
  GameObject go = temp[i] as GameObject;
  //go...
}

 

제네릭컬렉션으로 캐쉬하기top

일단 로딩한 후에는 해당 객체를 반복해서 사용하는 경우가 많습니다. 예를 들어 R1(녹색큐브)를 10개 복제한다고 하면 GameObject.Instantiate를 사용하기 위해 개별 객체를 인식해두지 않으면 안됩니다.

로딩된 배열을 해시테이블(HashTable)로 잡아두면 편리하게 사용할 수 있지만 제네릭 타입으로 정리하면 형변환도 필요없게 되므로 더욱 안정적이고 간단히 사용할 수 있을 것 입니다.

일단 제네릭컬렉션을 사용하려면 네임스페이스를 불러와야겠죠.

using System.Collections.Generic;

다음은 RSC클래스에 간단히 정적 필드로 캐쉬 공간을 만들어 둡시다.

class RSC {
  static private Dictionary<string,GameObject> _cache =
      new Dictionary<string,GameObject>();

 

로딩하기와 캐쉬에서 꺼내기top

그럼 이제 서브폴더를 로딩하는 함수를 일반화하여 캐쉬에 곧장 넣어버리죠.

static public void Load( string subfolder ){
  object[] t0 = Resources.LoadAll( subfolder );
  forint i = 0 ; i < t0.Length ; i++ ){
    GameObject t1 = (GameObject)(t0[i]);
    _cache[t1.name] = t1;
  }
 }
 
//서브폴더 로딩하기
RSC.Load( "block" );

그럼 그 뒤로는 캐쉬에서 직접 꺼내가던가 아니면 인스턴스를 반환하는 함수를 생각해 볼 수 있습니다. 우선 간단히 가져가는 함수는 다음과 같습니다.

static public GameObject Get( string key ){
  return _cache[key];
}
 
// R1을 가져오자
GameObject temp = RSC.Get( "R1" );

인스턴스를 직접 반환받고 싶다면 다음과 같은 함수로 편리하게 사용합시다.

static public GameObject Instance( string key, string name,
    float x, float y, float z ){
  GameObject t0 = (GameObject)(GameObject.Instantiate(
      _cache[key], new Vector3( x, y, z ), Quaternion.identity
    ));
  t0.name = name;
  return t0;
}
 
//R1으로부터 R1Clone을 가져오자.
GameObject temp = RSC.Instance( "R1""R1Clone", 0, 0, 0 );

 

캐쉬를 메모리에서 삭제top

메모리 절약을 위해 다양한 리소스를 로딩하려면 필요없는 리소스는 다시 삭제해줘야합니다. params인자를 통해 간단히 삭제하는 함수를 구현합시다.

static public void Remove( params string[] arg ){
  forint i = 0 ; i < arg.Length ; i++ ){
    string key = arg[i];
    _cache.Remove( key );
  }
}
 
//R1~R3까지 삭제
 
RSC.Remove( "R1""R2""R3" );

이를 통해 간단히 메모리에서 몇 개라도 한꺼번에 삭제할 수 있게 되었습니다[1. 참고로 GameObject의 경우 Resources.UnloadAsset 으로 삭제되지 않고 더 이상 사용되지 않으면 GC가 될 뿐입니다.]

 

간단한 샘플top

이상의 내용을 바탕으로 간단히 큐브를 화면에 채워봅시다.

Load ("block");
forint i = 0 ; i < 10 ; i++ ){
  forint j = 0 ; j < 10 ; j++ ){
    Instance( "R" + Random.Range(1,6), i+":"+j, i-5, j-5, 0 );
  }
}
Remove( "R1""R2" );

다음과 같은 화면이 된다면 정상입니다.

Screenshot_2

 

결론top

이번 포스팅에서는 간단히 런타임 에셋로딩을 알아봤습니다. 실제 소스는 거의 GameObject 로딩 중심으로 가볍게 설명했으나 이를 바탕으로 다양한 에셋로더를 구현하는 것은 또 다른 기회에 해보겠습니다(과연..)

종합 소스는 아래에 있습니다만, resources/block 폴더를 만들고 R1~R5까지의 색깔별 큐브를 프리펩으로 만들지 않으면 실행되지 않습니다.



using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class bside002 : MonoBehaviour {
	
	static private Dictionary<string,GameObject> _cache = new Dictionary<string,GameObject>();
	
	static public void Load( string subfolder ){
		object[] t0 = Resources.LoadAll( subfolder );
		for( int i = 0 ; i < t0.Length ; i++ ){
			GameObject t1 = (GameObject)(t0[i]);
			_cache[t1.name] = t1;
		}
	}
	static public GameObject Get( string key ){
		return _cache[key];
	}
	static public GameObject Instance( string key, string name, float x, float y, float z ){
		GameObject t0 = (GameObject)(GameObject.Instantiate( _cache[key], new Vector3( x, y, z ), Quaternion.identity ));
		t0.name = name;
		return t0;
	}
	static public void Remove( params string[] arg ){
		for( int i = 0 ; i < arg.Length ; i++ ){
			string key = arg[i];
			_cache.Remove( key );
		}
	}

	void Start () {
		Load ("block");
		for( int i = 0 ; i < 10 ; i++ ){
			for( int j = 0 ; j < 10 ; j++ ){
				Instance( "R" + Random.Range( 1, 6 ), i + "_" + j, i - 5, j - 5, 0 );
			}
		}
		Remove( "R1", "R2" );
	}
}




http://www.bsidesoft.com/?p=215





반응형

+ Recent posts