Skip to content

Commit a5f8b41

Browse files
committed
v1.0を登録
0 parents  commit a5f8b41

File tree

5 files changed

+286
-0
lines changed

5 files changed

+286
-0
lines changed

Editor.meta

Lines changed: 8 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
using System;
2+
using System.Collections;
3+
using System.Collections.Generic;
4+
using System.IO;
5+
using System.Text;
6+
using System.Linq;
7+
using System.Reflection;
8+
using UnityEngine;
9+
using UnityEditor;
10+
using YamlDotNet.RepresentationModel;
11+
12+
public static class AnimatorControllerCleaner{
13+
private const string YamlHeader = "%YAML 1.1\n%TAG !u! tag:unity3d.com,2011:\n";
14+
private const string ObjectHeaderPrefix = "--- !u!";
15+
16+
[MenuItem("Assets/CleanAnimatorControllers")]
17+
private static void CleanAnimatorContollers(){
18+
var Assets = Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets);
19+
if(Assets.Length == 0) return;
20+
foreach(var Asset in Assets){
21+
// Debug.Log(Asset);
22+
var path = AssetDatabase.GetAssetPath(Asset);
23+
// Debug.Log(path);
24+
if(path.EndsWith(".controller")){
25+
var pathArray = new string[] {path};
26+
AssetDatabase.ForceReserializeAssets(pathArray);
27+
CleanAnimatorController(path);
28+
}
29+
}
30+
AssetDatabase.Refresh();
31+
}
32+
33+
private static void CleanAnimatorController(string path){
34+
var objList = new List<AnimatorClassObj>();
35+
// データの取得
36+
foreach(var obj in ParseAnimator(path)){
37+
// Debug.Log(obj);
38+
objList.Add(obj);
39+
}
40+
bool[] refArray = new bool[objList.Count];
41+
for(int i=0; i<objList.Count; ++i){
42+
refArray[i] = false;
43+
}
44+
45+
// AnimatorControllerを起点に、参照されているオブジェクトをチェックする。
46+
for(int i=0; i<objList.Count; ++i)
47+
{
48+
var obj = objList[i];
49+
if(obj.classID == 91){
50+
SearchReference(i, ref objList, ref refArray);
51+
}
52+
}
53+
54+
// // refArrayがfalseのままのobjは参照されていない
55+
// for(int i=0; i<objList.Count; ++i)
56+
// {
57+
// if(!refArray[i]){
58+
// var obj = objList[i];
59+
// var classID = obj.classID;
60+
// var fileID = obj.fileID;
61+
// var root = ((YamlMappingNode)obj.node).Children.First();
62+
// var rootName = root.Key.ToString();
63+
// Debug.Log(
64+
// $"obj No.{i} is not referenced.\n"
65+
// + $"(classID:{classID} ,fileID:{fileID}, name:{rootName})");
66+
// }
67+
// }
68+
69+
// refArrayがtrueのオブジェクトのみを書き出し
70+
var objListFiltered = objList.Where((value, index) => refArray[index]);
71+
var yaml = PresentAnimator(objListFiltered.ToList());
72+
73+
File.WriteAllText(path, yaml);
74+
}
75+
76+
77+
struct AnimatorClassObj{
78+
public int classID;
79+
public string fileID;
80+
public YamlNode node;
81+
public string content;
82+
83+
public AnimatorClassObj(
84+
int classID, string fileID,
85+
YamlNode node, string content)
86+
{
87+
this.classID = classID;
88+
this.fileID = fileID;
89+
this.node = node;
90+
this.content = content;
91+
}
92+
}
93+
94+
private static void SearchReference(int i, ref List<AnimatorClassObj> objList, ref bool[] refArray)
95+
{
96+
if(refArray[i]) return;
97+
refArray[i] = true;
98+
AnimatorClassObj obj = objList[i];
99+
100+
// classID is documented on https://docs.unity3d.com/ja/2019.4/Manual/ClassIDReference.html
101+
switch(obj.classID)
102+
{
103+
case 91: // AnimatorController
104+
case 114: // MonoBehaviour
105+
case 206: // BlendTree
106+
case 1101: // AnimatorStateTransition
107+
case 1102: // AnimatorState
108+
case 1107: // AnimatorStateMachine
109+
case 1109: // AnimatorTransition
110+
case 1111: // AnimatorTransitionBase
111+
break;
112+
default:
113+
Debug.LogWarning("unexcepted classID "+obj.classID);
114+
throw new System.Exception("unexcepted classID "+obj.classID);
115+
}
116+
// obj内のfileID要素を辿る
117+
foreach(var fileID in RecursiveSearch(obj.node, "fileID")){
118+
// Debug.Log(fileID);
119+
if (fileID.GetType() == typeof(string))
120+
{
121+
var matchObj = objList.Where(x => x.fileID == (string)fileID);
122+
if (matchObj.Count() != 0)
123+
{
124+
var j = objList.IndexOf(matchObj.First());
125+
SearchReference(j, ref objList, ref refArray);
126+
}
127+
}
128+
}
129+
}
130+
131+
public static IEnumerable<string> RecursiveSearch(YamlNode node, string key)
132+
{
133+
switch (node)
134+
{
135+
case YamlMappingNode mappingNode:
136+
foreach (var childNode in mappingNode.Children)
137+
{
138+
if (childNode.Key.ToString() == key)
139+
{
140+
yield return childNode.Value.ToString();
141+
}
142+
143+
foreach (var result in RecursiveSearch(childNode.Value, key))
144+
{
145+
yield return result;
146+
}
147+
}
148+
break;
149+
150+
case YamlSequenceNode sequenceNode:
151+
foreach (var childNode in sequenceNode.Children)
152+
{
153+
foreach (var result in RecursiveSearch(childNode, key))
154+
{
155+
yield return result;
156+
}
157+
}
158+
break;
159+
}
160+
}
161+
162+
private static int GetClassIDByObjectHeader(string objectHeader)
163+
{
164+
return int.Parse(objectHeader.Substring(ObjectHeaderPrefix.Length).Split(' ')[0]);
165+
}
166+
167+
private static string GetFileIDByObjectHeader(string objectHeader)
168+
{
169+
return objectHeader.Substring(ObjectHeaderPrefix.Length).Split(' ')[1].Substring(1);
170+
}
171+
172+
private static IEnumerable<AnimatorClassObj> ParseAnimator(string yamlPath)
173+
{
174+
var lines = File.ReadLines(yamlPath);
175+
var sb = new StringBuilder();
176+
string objectHeader = null;
177+
string content = null;
178+
foreach(var line in lines)
179+
{
180+
if(line.StartsWith(ObjectHeaderPrefix))
181+
{
182+
if(objectHeader != null){
183+
content = sb.ToString();
184+
yield return new AnimatorClassObj(
185+
GetClassIDByObjectHeader(objectHeader),
186+
GetFileIDByObjectHeader(objectHeader),
187+
ParseYaml(content),
188+
content
189+
);
190+
sb.Clear();
191+
}
192+
objectHeader = line;
193+
continue;
194+
}
195+
196+
// 最初の2行については、まだobjectHeaderが出てきていないので読み飛ばす。
197+
if(objectHeader != null) sb.Append(line + "\n");
198+
}
199+
if(objectHeader == null) yield break;
200+
var text = sb.ToString();
201+
yield return new AnimatorClassObj(
202+
GetClassIDByObjectHeader(objectHeader),
203+
GetFileIDByObjectHeader(objectHeader),
204+
ParseYaml(content),
205+
content
206+
);
207+
}
208+
209+
private static YamlNode ParseYaml(string text)
210+
{
211+
var yamlStream = new YamlStream();
212+
var sr = new StringReader(text);
213+
yamlStream.Load(sr);
214+
return yamlStream.Documents[0].RootNode;
215+
}
216+
217+
private static string PresentYaml(YamlNode node)
218+
{
219+
var yamlDocument = new YamlDocument(node);
220+
var yamlStream = new YamlStream(yamlDocument);
221+
var sw = new StringWriter();
222+
sw.NewLine = "\n";
223+
yamlStream.Save(sw);
224+
return sw.ToString();
225+
}
226+
227+
private static string PresentAnimator(List<AnimatorClassObj> objList)
228+
{
229+
var sw = new StringWriter();
230+
sw.Write(YamlHeader);
231+
foreach(var obj in objList){
232+
sw.Write(ObjectHeaderPrefix + obj.classID + " &" + obj.fileID + "\n");
233+
sw.Write(obj.content);
234+
}
235+
return sw.ToString();
236+
}
237+
}

Editor/AnimatorControllerCleaner.cs.meta

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ReadMe.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# AnimatorControllerCleaner v1.0
2+
UnityのAnimatorController内に参照されないのに残ってしまったデータを削除します。
3+
4+
## 動作確認環境
5+
* Unity2019.4.31f1
6+
* VRChat Package Resolver Tool 0.1.19<br>
7+
(YamlDotNetに依存していますが、`Packages/com.vrchat.core.vpm-resolver/Editor/Dependencies/YamlDotNet.dll`に含まれているのでこれを使います。)<br>
8+
VCCを使っていない場合はAsset StoreからYamlDotNet for UnityをImportしてください。
9+
10+
## 使い方
11+
1. `Edit > Project Settings... > Editor > Asset Serialization > Mode`をForce Textに設定。
12+
2. AnimatorControllerCleaner.unitypackageをimport
13+
3. ゴミを削除したいAnimatorControllerをProject上で選択して右クリック `CleanAnimatorControllers`を実行。
14+
15+
## 何を行っているのか
16+
AnimatorControllerのAssetをTextModeで保存した場合、Yaml形式となっています。
17+
18+
Yamlをパースし、Root要素であるAnimatorControllerから参照しているオブジェクトを辿って行きます。
19+
20+
AnimatorControllerから辿った参照ツリーに含まれていないオブジェクトを除外してYamlファイルを保存しなおしています。
21+
22+
## 注意事項
23+
テストは行っていますが、完璧な動作は保証できません。一度AnimatorControllerを複製した上で実行し、問題ないことを確認することをおすすめします。

ReadMe.md.meta

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)