依存関係に基づいてスクリプト実行順設定を自動化する
Unityでは基本的にスクリプトの処理順番は不定なので、スクリプト間に依存関係があるときはAwakeやStart、UpdateやLateUpdateの違いで乗り切る。最後の手段としてScript Execution Orderを使う。依存関係に敏感な実装にしない。
というのがよくあるスタイルと思う。しかし処理順が問題でUpdateからLateUpdateにコードを移すとき、誤魔化しを感じることがあった。A <-- B <-- C のように2段の依存を見つけたときは特に。
そこで依存関係は明確にして、処理順は適切にコントロールする。という発想に切り替えてそれを補助するユーティリティを作ってみた。
DependsOnAttribute.cs
using System;
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
public class DependsOnAttribute : Attribute {
public Type dependee;
public DependsOnAttribute(Type dependee) {
this.dependee = dependee;
}
}
Editor/ExecutionOrderCoordinator.cs
using UnityEditor;
using UnityEngine;
using System;
using System.Collections.Generic;
using System.Linq;
[InitializeOnLoad]
public class ExecutionOrderCoordinator {
static ExecutionOrderCoordinator() {
EditorApplication.update += DoRefreshExecutionOrders;
}
static void DoRefreshExecutionOrders() {
EditorApplication.update -= DoRefreshExecutionOrders;
if (!EditorApplication.isPlaying) {
RefreshExecutionOrders();
}
}
static void RefreshExecutionOrders() {
var graph = GetDependencyGraph();
List<Type> sorted = TSort(graph.dependencies);
for (int i = 0; i < sorted.Count; ++i) {
var type = sorted[i];
var script = graph.monoScripts[type];
var order = i - sorted.Count;
if (MonoImporter.GetExecutionOrder(script) != order) {
MonoImporter.SetExecutionOrder(script, order);
}
}
}
class DependencyGraph {
public Dictionary<Type, MonoScript> monoScripts = new Dictionary<Type, MonoScript>();
public Dictionary<Type, HashSet<Type>> dependencies = new Dictionary<Type, HashSet<Type>>();
}
static DependencyGraph GetDependencyGraph() {
var graph = new DependencyGraph();
foreach (var script in MonoImporter.GetAllRuntimeMonoScripts()) {
var klass = script.GetClass();
if (klass != null) {
graph.monoScripts[klass] = script;
var attrs = Attribute.GetCustomAttributes(klass, typeof(DependsOnAttribute));
foreach (DependsOnAttribute attr in attrs) {
HashSet<Type> set;
if (!graph.dependencies.TryGetValue(klass, out set)) {
set = graph.dependencies[klass] = new HashSet<Type>();
}
set.Add(attr.dependee);
if (!graph.dependencies.ContainsKey(attr.dependee)) {
graph.dependencies[attr.dependee] = new HashSet<Type>();
}
}
}
}
return graph;
}
// The AWK Programming Language(1988) SECTION 7.3 TOPOLOGICAL SORTING
// http://d.hatena.ne.jp/tociyuki/20120919
// Color definition: white=0, gray=1, black=2
static List<Type> Depends(Type target, Dictionary<Type, HashSet<Type>> dependencies, Dictionary<Type, int> marked, List<Type> sorted) {
if (marked.ContainsKey(target)) {
return null;
}
marked[target] = 1;
foreach (var m in dependencies[target].OrderBy(x => x.FullName)) {
if (!marked.ContainsKey(m)) {
Depends(m, dependencies, marked, sorted);
} else if (marked[m] == 1) {
throw new InvalidOperationException(string.Format("nodes {0} and {1} are in a cycle", m, target));
}
}
sorted.Add(target);
marked[target] = 2;
return sorted;
}
static List<Type> TSort(Dictionary<Type, HashSet<Type>> dependencies) {
var marked = new Dictionary<Type, int>();
var sorted = new List<Type>();
foreach (var target in dependencies.Keys.OrderBy(x => x.FullName)) {
Depends(target, dependencies, marked, sorted);
}
return sorted;
}
}
Wikipediaから図を拝借。
上図のような依存関係があるとして、
C7.cs
[DependsOn(typeof(C8))]
[DependsOn(typeof(C11))]
public class C7 : MonoBehaviour { }
C8.cs
[DependsOn(typeof(C9))]
public class C8 : MonoBehaviour { }
このように依存関係を属性で定義していく。
スクリプトがビルドされると上図のようにScript Execution Orderが-1から負方向に向かって自動設定される。
このユーティリティを使うときは、手動で設定された順序と競合しないように、値を調整しておく必要がある(UIStretchなど)。
Author And Source
この問題について(依存関係に基づいてスクリプト実行順設定を自動化する), 我々は、より多くの情報をここで見つけました https://qiita.com/nobutaka-takushima/items/bb0b07481c24b0c22471著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .