Skip to content

Instantly share code, notes, and snippets.

@baba-s
Created August 29, 2017 02:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baba-s/309f2b30e342b8f0a3795cd59fcfc621 to your computer and use it in GitHub Desktop.
Save baba-s/309f2b30e342b8f0a3795cd59fcfc621 to your computer and use it in GitHub Desktop.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEditor;
/// <summary>
/// スプライト名を管理するクラスを作成するエディタ拡張
/// </summary>
public static class SpriteNameCreator
{
//====================================================================================
// クラス
//====================================================================================
/// <summary>
/// 書式指定文字列の情報を管理するクラス
/// </summary>
private sealed class FormatData
{
private readonly string m_format ;
private readonly string m_name ;
private readonly string m_argument ;
private readonly string m_arg0 ;
public string Format { get { return m_format ; } }
public string Name { get { return m_name ; } }
public string Argument { get { return m_argument ; } }
public string Arg0 { get { return m_arg0 ; } }
/// <summary>
/// コンストラクタ
/// </summary>
public FormatData( int count, string name, string label )
{
m_format = name;
m_name = label
.Replace( "/", "_" )
.Trim( '_' )
.Replace( "__", "_" )
;
var range = Enumerable.Range( 0, count );
m_argument = string.Join( ", ", range.Select( c => "object arg" + c ).ToArray() );
m_arg0 = string.Join( ", ", range.Select( c => "arg" + c ).ToArray() );
}
}
/// <summary>
/// FormatData の等価比較を管理するクラス
/// </summary>
private sealed class FormatDataComparer : IEqualityComparer<FormatData>
{
/// <summary>
/// 指定したオブジェクトが等しいかどうかを判断します
/// </summary>
public bool Equals( FormatData x, FormatData y )
{
return x.Name == y.Name;
}
/// <summary>
/// 指定したオブジェクトのハッシュ コードを返します
/// </summary>
public int GetHashCode( FormatData obj )
{
return obj.Name.GetHashCode();
}
}
//====================================================================================
// 定数(const)
//====================================================================================
private const string ITEM_NAME = "Tools/Create Sprite Name";
private const string PATH = "Assets/Scripts/SpriteName.cs";
//====================================================================================
// 定数(static readonly)
//====================================================================================
private static readonly string[] ATLAS_PATH_LIST = new []
{
"Assets/Atlases/",
};
private static readonly string[] INVARID_VARIABLE_STRINGS =
{
" ", "!", "\"", "#", "$",
"%", "&", "\'", "(", ")",
"-", "=", "^", "~", "\\",
"|", "[", "{", "@", "`",
"]", "}", ":", "*", ";",
"+", "/", "?", ".", ">",
",", "<"
};
//====================================================================================
// 定数
//====================================================================================
/// <summary>
/// スプライト名を管理するクラスを作成します
/// </summary>
[MenuItem( ITEM_NAME )]
private static void Create()
{
var builder = new StringBuilder();
foreach ( var atlasPath in ATLAS_PATH_LIST )
{
var folderNameList = GetFolderNames( atlasPath ).ToArray();
for ( int i = 0; i < folderNameList.Length; i++ )
{
var folderPath = folderNameList[ i ];
var atlasName = Path.GetFileNameWithoutExtension( folderPath );
var fullPath = atlasPath + Path.Combine( folderPath, atlasName ) + ".prefab";
var data = AssetDatabase.LoadAssetAtPath<UIAtlas>( fullPath );
if ( data == null ) continue;
EditorUtility.DisplayProgressBar
(
title : "SpriteNameCreator",
info : string.Format( "{0}/{1} : {2}", i, folderNameList.Length, atlasName ),
progress : ( float )i / folderNameList.Length
);
builder.AppendLine( "/// <summary>" );
builder.AppendFormat( "/// {0} のスプライト名を管理するクラス", atlasName ).AppendLine();
builder.AppendLine( "/// </summary>" );
builder.AppendFormat( "public static partial class {0}", atlasName ).AppendLine();
builder.AppendLine( "{" );
foreach ( var spriteData in data.spriteList )
{
var spriteName = spriteData.name;
var match = Regex.Match( spriteData.name, "^[0-9]" );
var paramName = match.Success ? "_" + spriteName : spriteName;
builder.Append( "\t" ).AppendFormat( @"public const string {0} = ""{1}"";", RemoveInvalidStrings( paramName ), spriteName ).AppendLine();
}
builder.Append( "\t" ).AppendLine();
var names = data.spriteList.Select( c => c.name ).ToArray();
foreach ( var n in ToFormatDataSequence( names ).Distinct( new FormatDataComparer() ) )
{
builder.Append( "\t" ).AppendFormat( @"/// <summary>""{0}""</summary>", n.Format ).AppendLine();
builder.Append( "\t" ).AppendFormat( "public static string get_{0}( {1} ) {{", n.Name, n.Argument ).AppendLine();
builder.Append( "\t\t" ).AppendFormat( @"return string.Format( ""{0}"", {1} );", n.Format, n.Arg0 ).AppendLine();
builder.Append( "\t" ).AppendLine( "}" );
}
builder.Append( "\t" ).AppendLine();
builder.Append( "}" );
}
}
File.WriteAllText( PATH, builder.ToString(), Encoding.UTF8 );
AssetDatabase.Refresh( ImportAssetOptions.ImportRecursive );
EditorUtility.ClearProgressBar();
EditorUtility.DisplayDialog( Path.GetFileName( PATH ), "作成が完了しました", "OK" );
var result = AssetDatabase.LoadAssetAtPath<MonoScript>( PATH );
EditorGUIUtility.PingObject( result );
}
/// <summary>
/// 指定されたディレクトリに存在する内に存在する第一階層フォルダの名前を全て取得します
/// </summary>
private static IEnumerable<string> GetFolderNames( string path )
{
return Directory
.GetDirectories( path, "*", SearchOption.AllDirectories )
.Select( c => Path.GetFileNameWithoutExtension( c ) )
.Where( c => c != "res" )
.Select( c => c.Replace( path, "" ) )
;
}
/// <summary>
/// 指定された文字列から変数名に使用できない文字を削除して返します
/// </summary>
private static string RemoveInvalidStrings( string str )
{
foreach ( var n in INVARID_VARIABLE_STRINGS )
{
str = str.Replace( n, string.Empty );
}
return str;
}
/// <summary>
/// スプライト名のリストを FormatData のシーケンスに変換して返します
/// </summary>
private static IEnumerable<FormatData> ToFormatDataSequence( IEnumerable<string> spriteNames )
{
foreach ( var n in GetFormattableTextList( "_", spriteNames.ToArray() ) )
{
var matches = Regex.Matches( n, @"{[0-9]}" );
if ( matches.Count <= 0 ) continue;
var safeLabel = Regex.Replace( n, @"{[0-9]}", string.Empty );
if ( string.IsNullOrEmpty( safeLabel ) ) continue;
yield return new FormatData( matches.Count, n, safeLabel );
}
}
/// <summary>
/// 特定の文字で区切られた文字列の配列から、複合書式指定文字列の配列を返します。
/// </summary>
/// <param name="separator">区切り文字</param>
/// <param name="texts">特定の文字で区切られた文字列の配列</param>
/// <returns>複合書式指定文字列の配列</returns>
/// <example>
/// var texts = new []
/// {
/// "Label_Start",
/// "Label_End",
/// "Label_Rea_Close",
/// "Label_Rea_Start",
/// "Label_Ten_Close",
/// "Label_Ten_Start",
/// };
/// var formattableTexts = GetFormattableTexts( "_", texts );
/// // Label_{0}
/// // Label_Rea_{0}
/// // Label_{0}_Close
/// // Label_{0}_{1}
/// // Label_{0}_Start
/// // Label_Ten_{0}
/// </example>
private static string[] GetFormattableTextList( string separator, params string[] texts )
{
var list = new List<string>();
for ( int i = 0; i < texts.Length - 1; i++ )
{
var separatedText1 = texts[ i ].Split( new []{ separator }, StringSplitOptions.None ).ToArray();
for ( int j = i + 1; j < texts.Length; j++ )
{
var separatedText2 = texts[ j ].Split( new []{ separator }, StringSplitOptions.None ).ToArray();
int argumentCount = 0;
var builder = new StringBuilder();
for ( int k = 0; k < Math.Min( separatedText1.Length, separatedText2.Length ); k++ )
{
var text1 = separatedText1[ k ];
var text2 = separatedText2[ k ];
if ( text1 == text2 )
{
builder.Append( text1 );
}
else
{
builder.AppendFormat( "{{{0}}}", argumentCount++ );
}
builder.Append( "_" );
}
builder = builder.Remove( builder.Length - 1, 1 );
list.Add( builder.ToString() );
}
}
return list.Distinct().ToArray();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment