※아래의 소스는 NGUI를 사용한 프로젝트에서 사용할수 있습니다.
필자의 프로젝트가 길지 않은 시간동안 개발을 진행하면서 UI가 빈번하게 변경되다보니
아틀라스내의 사용하지 않는 스프라이트들이 많아지게 되었습니다.
일일히 프리팹을 뒤져가면서 하나씩 사용하는지 사용하지 않는지 체크하는건 너무 비효율적이고 좋은 방법이 없을까 고민하다가
특정 아틀라스를 선택 후 특정 폴더를 지정해서 그 폴더내의 prefab들에서 특정아틀라스의 스프라이트들이 사용중인지 검출하는 툴을 만들게 되었습니다.
구조는 간단합니다.
우선 특정 아틀라스 파일을 StreamReader 로 읽어들여서 문자열로 읽어옵니다.
참고로 아틀라스내의 스프라이트들은 name: xxx 의 형태로 저장되기 때문에 아틀라스 prefab 내에서 name: 이라는 스트링배열을 찾은후 그 다음스트링배열의 값을 dictionary 자료구조에 add 시켜놓습니다.(dictionary 를 사용한 이유는 조금이라도 검출을 빠르게 하기 위해서입니다.)
특정 아틀라스의 모든 스프라이트들을 add 시켜놓은 상태에서 특정 폴더를 지정해서 하위폴더롤 모두 포함하여 prefab들을 찾아내고 해당 prefab역시 StreamReader을 통해 문자열로 읽어옵니다.
일반적인 prefab에서 스프라이트는 mSpriteName: 라는 변수에 저장되어 있으므로 스트링 배열중에서 mSpriteName: 다음 배열값을 기존에 저장했던 dictionary에서 찾아서 삭제를 해줍니다.
필자의 프로젝트에서는 테이블(*.xml) 또는 소스(*.cs)에서도 스프라이트 경로를 지정하는 경우도 있어서 xml 및 cs파일들도 죄다 검출해서 사용여부를 확인하게끔 하였습니다.
다만 xml과 cs에서는 조금 다른 방식으로 검출을 시도하였는데.
위의 방법을 통해 만들어진 리스트로 RegularExpressions(Regex)를 이용한 파서를 만든뒤 MatchCollection을 이용하여 사용하는 스프라이트를 다시 한번 검출하도록 하였습니다.
다만 처음의 방식에 비해 MatchCollection이 부하가 더 큰편이므로 검출이 빈번한 경우에는 사용하지 않는것을 추천드립니다.
위와 같은 로직을 통하여서 기존의 dictionary에는 삭제되지 않은(사용하지 않는) 스프라이트들이 남게되고 해당 목록들을 txt 파일로 저장하였습니다.
누군가 사용할지는 모르겠지만...개인적으로 나중에 또 쓸일이 있을것 같아 포스팅 해둡니다~!!
using System.Text.RegularExpressions;
[MenuItem("ArkEditor/CheckSprite")]
static void CheckSprite()
{
// 사용하지 않는 아틀라스 목록을 추출할 아틀라스 프리팹 파일을 선택.
string FolderPath = "../Scenes_work/UIRenewalWork/ARK_atlas";
var path = UnityEditor.EditorUtility.OpenFilePanel(
"1. Select AtlasPrefab",
FolderPath,
"prefab");
if (string.IsNullOrEmpty(path) == true)
return;
StreamReader reader = new StreamReader(path, true);
if (reader == null)
return;
Dictionary<string, string> spriteList = new Dictionary<string, string>();
string strTemp = reader.ReadToEnd();
Regex rgSpace = new Regex(" ");
Regex rgEnter = new Regex("\n");
string[] texts = rgSpace.Split(strTemp);
// 아틀라스 prefab에서 스프라이트 이름이 "name:" 뒤에 위치하므로 name: 위치 에서 +1 한 값을 맵에 add
int nIndex = 0;
foreach( var text in texts )
{
if( text.CompareTo("name:") == 0 )
{
// 스프라이트 이름 뒤에 개행문자(\n)가 들어가있으므로 해당 문자를 삭제함.
string temp = rgEnter.Replace(texts[nIndex + 1], "");
if( spriteList.ContainsKey(temp) == false )
{
spriteList.Add(temp,temp);
}
}
nIndex++;
}
var check_dir = UnityEditor.EditorUtility.OpenFolderPanel("2. Select Check Dir Folder", FolderPath, "trunk");
if (string.IsNullOrEmpty(check_dir) == true)
return;
var prefab_files = Directory.GetFiles(check_dir, "*.prefab", SearchOption.AllDirectories);
for (int index = 0; index < prefab_files.Length; ++index)
{
DEV.EditorLog(string.Format("{0} 작업중.. 남은 prefab 체크 파일 {1}", prefab_files[index], prefab_files.Length - index));
System.IO.StreamReader readerPrefab = new System.IO.StreamReader(prefab_files[index]);
string read_to_end = readerPrefab.ReadToEnd();
string[] prefabtexts = rgSpace.Split(read_to_end);
// 이 부분도 MatchCollection을 사용하려 했지만..부하가 너무 커서 그냥 아래와 같은 방식으로 사용함.
int tIndex = 0;
foreach (var text in prefabtexts)
{
if (text.CompareTo("mSpriteName:") == 0)
{
string temp = rgEnter.Replace(prefabtexts[tIndex + 1], "");
if (spriteList.ContainsKey(temp) == true)
{
spriteList.Remove(temp);
}
}
tIndex++;
}
}
// 부하를 조금이라도 줄이기 위해서 위에서 갱신된 리스트를 기준으로 파서를 만듬.
string strParser = "";
int nCount = 1;
foreach (var text in spriteList)
{
strParser += text.Value;
if (nCount != spriteList.Count)
{
strParser += "|";
}
nCount++;
}
Regex rgParser = new Regex(strParser);
var cs_files = Directory.GetFiles(check_dir, "*.cs", SearchOption.AllDirectories);
for (int index = 0; index < cs_files.Length; ++index)
{
DEV.EditorLog(string.Format("{0} 작업중.. 남은 cs 체크 파일 {1}", cs_files[index], cs_files.Length - index));
System.IO.StreamReader readerCs = new System.IO.StreamReader(cs_files[index]);
string read_to_end = readerCs.ReadToEnd();
MatchCollection mc = rgParser.Matches(read_to_end);
foreach( var match in mc )
{
if (spriteList.ContainsKey(match.ToString()) == true)
{
spriteList.Remove(match.ToString());
}
}
}
var xml_files = Directory.GetFiles(check_dir, "*.xml", SearchOption.AllDirectories);
for (int index = 0; index < xml_files.Length; ++index)
{
if (xml_files[index].Contains("LocaleString.") == false)
{
DEV.EditorLog(string.Format("{0} 작업중.. 남은 xml 체크 파일 {1}", xml_files[index], xml_files.Length - index));
System.IO.StreamReader readerXml = new System.IO.StreamReader(xml_files[index]);
string read_to_end = readerXml.ReadToEnd();
MatchCollection mc = rgParser.Matches(read_to_end);
foreach (var match in mc)
{
if (spriteList.ContainsKey(match.ToString()) == true)
{
spriteList.Remove(match.ToString());
}
}
}
}
DEV.EditorLog("체크 완료. 사용하지 않는 SpriteName을 파일로 생성중입니다");
string filename = check_dir + @"\not_use_SpriteNameList.txt";
var writeFile = FileIO.CreateResourceTextWriter(filename);
foreach( var sprName in spriteList )
{
writeFile.Writer.WriteLine(sprName.Value);
}
writeFile.Close();
DEV.EditorLog("{0}로 생성했습니다.", filename);
}
}
'프로그래밍 > Unity' 카테고리의 다른 글
유니티에서 싱글톤 사용하기 (0) | 2013.06.23 |
---|