2014. 10. 9. 15:00

* 2023-10-05 : 현재 이 스크립트는 유지보수가 되지 않고 있는 것으로 보입니다.*

대안에 대한 내용은 마지막에 넣어놨습니다.

 

 

이전 글에서 유니티 에디터에서 '파일 다얄로그'를 열어 파일을 선택하는 것을 했습니다.

문제는 유니티 에디터에서만 쓸 수 있다는 것이죠 ㅡ,.ㅡ;;;

 

그래서 검색해 보니 스크립트로 구현하는 방법이 있네요.

'ImprovedFileBrowser'라는 스크립트입니다.

(참고 : unify community - ImprovedFileBrowser )

 

 

1. 스크립트 생성하기

설명이 워낙 잘되어 있어서 전체 코드를 넣겠습니다.

이것은 픽스된 코드가 적용된 코드입니다.

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

/*
	File browser for selecting files or folders at runtime.
 */

public enum FileBrowserType
{
	File,
	Directory
}

public class FileBrowser
{

	// Called when the user clicks cancel or select
	public delegate void FinishedCallback(string path);
	// Defaults to working directory
	public string CurrentDirectory
	{
		get
		{
			return m_currentDirectory;
		}
		set
		{
			SetNewDirectory(value);
			SwitchDirectoryNow();
		}
	}
	protected string m_currentDirectory;
	// Optional pattern for filtering selectable files/folders. See:
	// http://msdn.microsoft.com/en-us/library/wz42302f(v=VS.90).aspx
	// and
	// http://msdn.microsoft.com/en-us/library/6ff71z1w(v=VS.90).aspx
	public string SelectionPattern
	{
		get
		{
			return m_filePattern;
		}
		set
		{
			m_filePattern = value;
			ReadDirectoryContents();
		}
	}
	protected string m_filePattern;

	// Optional image for directories
	public Texture2D DirectoryImage
	{
		get
		{
			return m_directoryImage;
		}
		set
		{
			m_directoryImage = value;
			BuildContent();
		}
	}
	protected Texture2D m_directoryImage;

	// Optional image for files
	public Texture2D FileImage
	{
		get
		{
			return m_fileImage;
		}
		set
		{
			m_fileImage = value;
			BuildContent();
		}
	}
	protected Texture2D m_fileImage;

	// Browser type. Defaults to File, but can be set to Folder
	public FileBrowserType BrowserType
	{
		get
		{
			return m_browserType;
		}
		set
		{
			m_browserType = value;
			ReadDirectoryContents();
		}
	}
	protected FileBrowserType m_browserType;
	protected string m_newDirectory;
	protected string[] m_currentDirectoryParts;

	protected string[] m_files;
	protected GUIContent[] m_filesWithImages;
	protected int m_selectedFile;

	protected string[] m_nonMatchingFiles;
	protected GUIContent[] m_nonMatchingFilesWithImages;
	protected int m_selectedNonMatchingDirectory;

	protected string[] m_directories;
	protected GUIContent[] m_directoriesWithImages;
	protected int m_selectedDirectory;

	protected string[] m_nonMatchingDirectories;
	protected GUIContent[] m_nonMatchingDirectoriesWithImages;

	protected bool m_currentDirectoryMatches;

	protected GUIStyle CentredText
	{
		get
		{
			if (m_centredText == null)
			{
				m_centredText = new GUIStyle(GUI.skin.label);
				m_centredText.alignment = TextAnchor.MiddleLeft;
				m_centredText.fixedHeight = GUI.skin.button.fixedHeight;
			}
			return m_centredText;
		}
	}
	protected GUIStyle m_centredText;

	protected string m_name;
	protected Rect m_screenRect;

	protected Vector2 m_scrollPosition;

	protected FinishedCallback m_callback;

	// Browsers need at least a rect, name and callback
	public FileBrowser(Rect screenRect, string name, FinishedCallback callback)
	{
		m_name = name;
		m_screenRect = screenRect;
		m_browserType = FileBrowserType.File;
		m_callback = callback;
		SetNewDirectory(Directory.GetCurrentDirectory());
		SwitchDirectoryNow();
	}

	protected void SetNewDirectory(string directory)
	{
		m_newDirectory = directory;
	}

	protected void SwitchDirectoryNow()
	{
		if (m_newDirectory == null || m_currentDirectory == m_newDirectory)
		{
			return;
		}
		m_currentDirectory = m_newDirectory;
		m_scrollPosition = Vector2.zero;
		m_selectedDirectory = m_selectedNonMatchingDirectory = m_selectedFile = -1;
		ReadDirectoryContents();
	}

	protected void ReadDirectoryContents()
	{
		if (m_currentDirectory == "/")
		{
			m_currentDirectoryParts = new string[] { "" };
			m_currentDirectoryMatches = false;
		}
		else
		{
			m_currentDirectoryParts = m_currentDirectory.Split(Path.DirectorySeparatorChar);
			if (SelectionPattern != null)
			{
				string directoryName = Path.GetDirectoryName(m_currentDirectory);
				string[] generation = new string[0];
				if (directoryName != null)
				{ 	//This is new: generation should be an empty array for the root directory.
					//directoryName will be null if it's a root directory
					generation = Directory.GetDirectories(
					directoryName,
					SelectionPattern);
				}
				m_currentDirectoryMatches = Array.IndexOf(generation, m_currentDirectory) >= 0;
			}
			else
			{
				m_currentDirectoryMatches = false;
			}
		}

		if (BrowserType == FileBrowserType.File || SelectionPattern == null)
		{
			m_directories = Directory.GetDirectories(m_currentDirectory);
			m_nonMatchingDirectories = new string[0];
		}
		else
		{
			m_directories = Directory.GetDirectories(m_currentDirectory, SelectionPattern);
			var nonMatchingDirectories = new List<string>();
			foreach (string directoryPath in Directory.GetDirectories(m_currentDirectory))
			{
				if (Array.IndexOf(m_directories, directoryPath) < 0)
				{
					nonMatchingDirectories.Add(directoryPath);
				}
			}
			m_nonMatchingDirectories = nonMatchingDirectories.ToArray();
			for (int i = 0; i < m_nonMatchingDirectories.Length; ++i)
			{
				int lastSeparator = m_nonMatchingDirectories[i].LastIndexOf(Path.DirectorySeparatorChar);
				m_nonMatchingDirectories[i] = m_nonMatchingDirectories[i].Substring(lastSeparator + 1);
			}
			Array.Sort(m_nonMatchingDirectories);
		}

		for (int i = 0; i < m_directories.Length; ++i)
		{
			m_directories[i] = m_directories[i].Substring(m_directories[i].LastIndexOf(Path.DirectorySeparatorChar) + 1);
		}

		if (BrowserType == FileBrowserType.Directory || SelectionPattern == null)
		{
			m_files = Directory.GetFiles(m_currentDirectory);
			m_nonMatchingFiles = new string[0];
		}
		else
		{
			m_files = Directory.GetFiles(m_currentDirectory, SelectionPattern);
			var nonMatchingFiles = new List<string>();
			foreach (string filePath in Directory.GetFiles(m_currentDirectory))
			{
				if (Array.IndexOf(m_files, filePath) < 0)
				{
					nonMatchingFiles.Add(filePath);
				}
			}
			m_nonMatchingFiles = nonMatchingFiles.ToArray();
			for (int i = 0; i < m_nonMatchingFiles.Length; ++i)
			{
				m_nonMatchingFiles[i] = Path.GetFileName(m_nonMatchingFiles[i]);
			}
			Array.Sort(m_nonMatchingFiles);
		}
		for (int i = 0; i < m_files.Length; ++i)
		{
			m_files[i] = Path.GetFileName(m_files[i]);
		}
		Array.Sort(m_files);
		BuildContent();
		m_newDirectory = null;
	}

	protected void BuildContent()
	{
		m_directoriesWithImages = new GUIContent[m_directories.Length];
		for (int i = 0; i < m_directoriesWithImages.Length; ++i)
		{
			m_directoriesWithImages[i] = new GUIContent(m_directories[i], DirectoryImage);
		}
		m_nonMatchingDirectoriesWithImages = new GUIContent[m_nonMatchingDirectories.Length];
		for (int i = 0; i < m_nonMatchingDirectoriesWithImages.Length; ++i)
		{
			m_nonMatchingDirectoriesWithImages[i] = new GUIContent(m_nonMatchingDirectories[i], DirectoryImage);
		}
		m_filesWithImages = new GUIContent[m_files.Length];
		for (int i = 0; i < m_filesWithImages.Length; ++i)
		{
			m_filesWithImages[i] = new GUIContent(m_files[i], FileImage);
		}
		m_nonMatchingFilesWithImages = new GUIContent[m_nonMatchingFiles.Length];
		for (int i = 0; i < m_nonMatchingFilesWithImages.Length; ++i)
		{
			m_nonMatchingFilesWithImages[i] = new GUIContent(m_nonMatchingFiles[i], FileImage);
		}
	}

	public void OnGUI()
	{
		GUILayout.BeginArea(
			m_screenRect,
			m_name,
			GUI.skin.window
		);
		GUILayout.BeginHorizontal();
		for (int parentIndex = 0; parentIndex < m_currentDirectoryParts.Length; ++parentIndex)
		{
			if (parentIndex == m_currentDirectoryParts.Length - 1)
			{
				GUILayout.Label(m_currentDirectoryParts[parentIndex], CentredText);
			}
			else if (GUILayout.Button(m_currentDirectoryParts[parentIndex]))
			{
				string parentDirectoryName = m_currentDirectory;
				for (int i = m_currentDirectoryParts.Length - 1; i > parentIndex; --i)
				{
					parentDirectoryName = Path.GetDirectoryName(parentDirectoryName);
				}
				SetNewDirectory(parentDirectoryName);
			}
		}
		GUILayout.FlexibleSpace();
		GUILayout.EndHorizontal();
		m_scrollPosition = GUILayout.BeginScrollView(
			m_scrollPosition,
			false,
			true,
			GUI.skin.horizontalScrollbar,
			GUI.skin.verticalScrollbar,
			GUI.skin.box
		);
		m_selectedDirectory = GUILayoutx.SelectionList(
			m_selectedDirectory,
			m_directoriesWithImages,
			DirectoryDoubleClickCallback
		);
		if (m_selectedDirectory > -1)
		{
			m_selectedFile = m_selectedNonMatchingDirectory = -1;
		}
		m_selectedNonMatchingDirectory = GUILayoutx.SelectionList(
			m_selectedNonMatchingDirectory,
			m_nonMatchingDirectoriesWithImages,
			NonMatchingDirectoryDoubleClickCallback
		);
		if (m_selectedNonMatchingDirectory > -1)
		{
			m_selectedDirectory = m_selectedFile = -1;
		}
		GUI.enabled = BrowserType == FileBrowserType.File;
		m_selectedFile = GUILayoutx.SelectionList(
			m_selectedFile,
			m_filesWithImages,
			FileDoubleClickCallback
		);
		GUI.enabled = true;
		if (m_selectedFile > -1)
		{
			m_selectedDirectory = m_selectedNonMatchingDirectory = -1;
		}
		GUI.enabled = false;
		GUILayoutx.SelectionList(
			-1,
			m_nonMatchingFilesWithImages
		);
		GUI.enabled = true;
		GUILayout.EndScrollView();
		GUILayout.BeginHorizontal();
		GUILayout.FlexibleSpace();
		if (GUILayout.Button("Cancel", GUILayout.Width(50)))
		{
			m_callback(null);
		}
		if (BrowserType == FileBrowserType.File)
		{
			GUI.enabled = m_selectedFile > -1;
		}
		else
		{
			if (SelectionPattern == null)
			{
				GUI.enabled = m_selectedDirectory > -1;
			}
			else
			{
				GUI.enabled = m_selectedDirectory > -1 ||
								(
									m_currentDirectoryMatches &&
									m_selectedNonMatchingDirectory == -1 &&
									m_selectedFile == -1
								);
			}
		}
		if (GUILayout.Button("Select", GUILayout.Width(50)))
		{
			if (BrowserType == FileBrowserType.File)
			{
				m_callback(Path.Combine(m_currentDirectory, m_files[m_selectedFile]));
			}
			else
			{
				if (m_selectedDirectory > -1)
				{
					m_callback(Path.Combine(m_currentDirectory, m_directories[m_selectedDirectory]));
				}
				else
				{
					m_callback(m_currentDirectory);
				}
			}
		}
		GUI.enabled = true;
		GUILayout.EndHorizontal();
		GUILayout.EndArea();

		if (Event.current.type == EventType.Repaint)
		{
			SwitchDirectoryNow();
		}
	}

	protected void FileDoubleClickCallback(int i)
	{
		if (BrowserType == FileBrowserType.File)
		{
			m_callback(Path.Combine(m_currentDirectory, m_files[i]));
		}
	}

	protected void DirectoryDoubleClickCallback(int i)
	{
		SetNewDirectory(Path.Combine(m_currentDirectory, m_directories[i]));
	}

	protected void NonMatchingDirectoryDoubleClickCallback(int i)
	{
		SetNewDirectory(Path.Combine(m_currentDirectory, m_nonMatchingDirectories[i]));
	}

}

 

(참고 : unify community - ImprovedFileBrowser )

 

'GUILayoutx'추가

위 코드를 빌드하면 'GUILayoutx'가 없다는 에러가 납니다.

'ImprovedSelectionList'라는 것을 추가해주어야 한다고 합니다.

(참고 : unify community - ImprovedSelectionList )

using UnityEngine;

public class GUILayoutx
{
	public delegate void DoubleClickCallback(int index);

	public static int SelectionList(int selected, GUIContent[] list)
	{
		//return SelectionList(selected, list, "List Item", null);
		return SelectionList(selected, list, "Label", null);
	}

	public static int SelectionList(int selected, GUIContent[] list, GUIStyle elementStyle)
	{
		return SelectionList(selected, list, elementStyle, null);
	}

	public static int SelectionList(int selected, GUIContent[] list, DoubleClickCallback callback)
	{
		//return SelectionList(selected, list, "List Item", callback);
		return SelectionList(selected, list, "Label", callback);
	}

	public static int SelectionList(int selected, GUIContent[] list, GUIStyle elementStyle, DoubleClickCallback callback)
	{
		for (int i = 0; i < list.Length; ++i)
		{
			Rect elementRect = GUILayoutUtility.GetRect(list[i], elementStyle);
			bool hover = elementRect.Contains(Event.current.mousePosition);
			if (hover && Event.current.type == EventType.MouseDown)
			{
				selected = i;
				Event.current.Use();
			}
			else if (hover && callback != null && Event.current.type == EventType.MouseUp && Event.current.clickCount == 2)
			{
				callback(i);
				Event.current.Use();
			}
			else if (Event.current.type == EventType.repaint)
			{
				elementStyle.Draw(elementRect, list[i], hover, false, i == selected, false);
			}
		}
		return selected;
	}

	public static int SelectionList(int selected, string[] list)
	{
		//return SelectionList(selected, list, "List Item", null);
		return SelectionList(selected, list, "Label", null);
	}

	public static int SelectionList(int selected, string[] list, GUIStyle elementStyle)
	{
		return SelectionList(selected, list, elementStyle, null);
	}

	public static int SelectionList(int selected, string[] list, DoubleClickCallback callback)
	{
		//return SelectionList(selected, list, "List Item", callback);
		return SelectionList(selected, list, "Label", callback);
	}

	public static int SelectionList(int selected, string[] list, GUIStyle elementStyle, DoubleClickCallback callback)
	{
		for (int i = 0; i < list.Length; ++i)
		{
			Rect elementRect = GUILayoutUtility.GetRect(new GUIContent(list[i]), elementStyle);
			bool hover = elementRect.Contains(Event.current.mousePosition);
			if (hover && Event.current.type == EventType.MouseDown && Event.current.clickCount == 1) // added " && Event.current.clickCount == 1"
			{
				selected = i;
				Event.current.Use();
			}
			else if (hover && callback != null && Event.current.type == EventType.MouseDown && Event.current.clickCount == 2) //Changed from MouseUp to MouseDown
			{
				Debug.Log("Works !");
				callback(i);
				Event.current.Use();
			}

			else if (Event.current.type == EventType.repaint)
			{
				elementStyle.Draw(elementRect, list[i], hover, false, i == selected, false);
			}
		}
		return selected;
	}
}

(참고 : unify community - ImprovedSelectionList )

 

"List Item"이 없어서 경고가 나옵니다.

( "Unable to find style 'List Item' in skin 'GameSkin' Layout" )

이 경고 때문에 "Label"로 변경합니다.

 

 

2. 사용하기

스크립트를 설명하는 페이지에 샘플도 잘 나와 있으니 그대로 따라 해 봅시다.

using UnityEngine;

public class TextFileFinder : MonoBehaviour
{

	protected string m_textPath;

	protected FileBrowser m_fileBrowser;

	[SerializeField]
	protected Texture2D m_directoryImage,
						m_fileImage;

	public Texture2D texture;
	public GameObject m_goCube;

	void Start()
	{
		m_goCube = GameObject.Find("Cube");
	}

	protected void OnGUI()
	{
		if (m_fileBrowser != null)
		{
			m_fileBrowser.OnGUI();
		}
		else
		{
			OnGUIMain();
		}
	}

	protected void OnGUIMain()
	{

		GUILayout.BeginHorizontal();
		GUILayout.Label("Text File", GUILayout.Width(100));
		GUILayout.FlexibleSpace();
		GUILayout.Label(m_textPath ?? "none selected");
		if (GUILayout.Button("...", GUILayout.ExpandWidth(false)))
		{
			m_fileBrowser = new FileBrowser(
				new Rect(10, 10, 600, 300),
				"Choose Text File",
				FileSelectedCallback
			);
			//m_fileBrowser.SelectionPattern = "*.png";
			m_fileBrowser.SelectionPattern = "*.txt";
			m_fileBrowser.DirectoryImage = m_directoryImage;
			m_fileBrowser.FileImage = m_fileImage;
		}
		GUILayout.EndHorizontal();
	}

	protected void FileSelectedCallback(string path)
	{
		m_fileBrowser = null;
		/*
		if (path.Length != 0)
		{
			WWW www = new WWW("file://" + path);
			texture = new Texture2D(64, 64);
			www.LoadImageIntoTexture(texture);
			m_goCube.renderer.material.mainTexture = texture;
		}*/

		m_textPath = path;
	}
}

(참고 : unify community - ImprovedFileBrowser )

 

제가 테스트용으로 사용한 큐브에 텍스쳐를 바꾸는 코드가 주석처리 되어 있습니다.

주석을 풀어 사용해 보세요.

 

 

3. 테스트

메인카메라에 스크립트를 추가하고 테스트해 봅시다.

(참고로 웹에서는 동작하지 않습니다.)

 

 

잘 동작하네요 ㅎㅎㅎ

 

 

마무리

당연하게도 웹에서는 동작하지 않습니다.

우분투에서는 테스트해 봤는데 잘됩니다.

FileBrowser.zip
다운로드

 

 

 

- 2023-10-05 추가

지금 날짜 기준으로 이 포스팅의 스크립트는 유지보수가 되지 않는 것으로 보입니다.

다른 스크립트를 이용할 것을 권장합니다.

 

다른 유니티 파일 브라우저 프로젝트

참고 : github - UnitySimpleFileBrowser, UnityStandaloneFileBrowser