前提・実現したいこと
例えばprefabファイルは public GameObject としてInspectorから取得できますが、同様にC#のスクリプトもInspectorから取得したいです。
用途としては取得したスクリプトのTypeを取得して、そのTypeのスクリプトがシーン中のオブジェクトにアタッチされていたら無効にしたいです。
C#スクリプトがシーン中のGameObjectにアタッチされている場合は、Public MonoBehaviour などで取得できると思いますが、シーン中には存在しないAssetsの下にあるスクリプトも取得したいです。
良い方法をご存じの方は教えてください。
試したこと
public MonoBehaviour ではAssetsの下のC#スクリプトをInspectorの変数へドラッグ&ドロップしようとしても設定することは出来ませんでした。
public UnityEngine.Object ならば可能でしたが、それではC#スクリプトのTypeが取得できません。
補足情報(FW/ツールのバージョンなど)
Unity 2018.4.19f1
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
十分に動作確認したわけではないのですが、MonoScript型を使ってみてはいかがでしょうか?
これはGetClassなんてメソッドを持っており、スクリプトファイルから対応するスクリプトコンポーネントの型を取得できそうです。
lang
1using System; 2using System.Linq; 3using UnityEngine; 4 5#if UNITY_EDITOR 6using UnityEditor; 7#endif 8 9public class ComponentDisabler : MonoBehaviour 10{ 11 #if UNITY_EDITOR 12 public MonoScript script; 13 14 private void Update() 15 { 16 if (Input.GetKeyDown(KeyCode.Space) && (this.script != null)) 17 { 18 this.DisableAllComponents(this.script.GetClass()); 19 } 20 } 21 22 private void DisableAllComponents(Type componentType) 23 { 24 if (componentType == null) 25 { 26 return; 27 } 28 29 // コンポーネントが「enabled」という名前の設定可能なパブリックboolプロパティを持っているか調べる 30 var enabledProperty = componentType.GetProperty("enabled"); 31 var propertyIsValid = (enabledProperty != null) && enabledProperty.CanWrite && (enabledProperty.PropertyType == typeof(bool)); 32 33 // コンポーネントが「enabled」という名前のreadonlyでないパブリックboolフィールドを持っているか調べる 34 var enabledField = componentType.GetField("enabled"); 35 var fieldIsValid = (enabledField != null) && !enabledField.IsInitOnly && (enabledField.FieldType == typeof(bool)); 36 37 // 適切なプロパティ、または適切なフィールドを持っていなければ処理を中断する 38 if (!propertyIsValid && !fieldIsValid) 39 { 40 return; 41 } 42 43 // enabledをfalseにする処理を記述する 44 var disableComponent = propertyIsValid 45 ? target => { enabledProperty.SetValue(target, false); } 46 : (Action<Component>)(target => { enabledField.SetValue(target, false); }); 47 48 // 現在ロードされているコンポーネントの中で、いずれかのシーン上に存在する(アセットでない)ものを抜き出して... 49 // ※2018.4.19f1とのことですので、FindObjectsOfType(componentType, false)の形は使用できないかもしれません 50 // その場合は、アクティブなゲームオブジェクトだけを処理できればいいのでしたらFindObjectsOfType(componentType)を使い、 51 // さもなければリファレンスの説明にあるようにResources.FindObjectsOfTypeAllを使い、その中からシーンに 52 // 所属していないオブジェクトを除外して処理対象にすることになるかと思います 53 foreach (var component in FindObjectsOfType(componentType, false).OfType<Component>()) 54 { 55 // それを無効化する 56 Debug.Log($"{componentType.Name} on {component.name} will be disabled."); 57 disableComponent(component); 58 } 59 } 60 #endif 61}
ただしMonoScript
はUnityEditor
ネームスペースにある型ですので、プレイモードでは使用可能だと思いますが、ビルドしたプログラム上では実行できないでしょうね。
苦肉の策として、「スクリプトファイルの名前とスクリプトコンポーネントの名前は一致するはず」と仮定して、下記のようにアセット名から型を探す形にしてみました。
lang
1using System; 2using System.Linq; 3using UnityEngine; 4 5public class ComponentDisabler2 : MonoBehaviour 6{ 7 // フィールドの型をObjectにする 8 public UnityEngine.Object target; 9 10 private void Update() 11 { 12 if (Input.GetKeyDown(KeyCode.Space) && (this.target != null)) 13 { 14 this.DisableAllComponents(GetType(this.target.name)); 15 } 16 } 17 18 // 名前がtypeNameの型を探す 19 private static Type GetType(string typeName) 20 { 21 return AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).FirstOrDefault(type => type.Name == typeName); 22 } 23 24 // DisableAllComponentsはComponentDisablerのものと同じ 25 private void DisableAllComponents(Type componentType) 26 { 27 if (componentType == null) 28 { 29 return; 30 } 31 32 var enabledProperty = componentType.GetProperty("enabled"); 33 var propertyIsValid = (enabledProperty != null) && enabledProperty.CanWrite && (enabledProperty.PropertyType == typeof(bool)); 34 35 var enabledField = componentType.GetField("enabled"); 36 var fieldIsValid = (enabledField != null) && !enabledField.IsInitOnly && (enabledField.FieldType == typeof(bool)); 37 38 if (!propertyIsValid && !fieldIsValid) 39 { 40 return; 41 } 42 43 var disableComponent = propertyIsValid 44 ? target => { enabledProperty.SetValue(target, false); } 45 : (Action<Component>)(target => { enabledField.SetValue(target, false); }); 46 47 foreach (var component in FindObjectsOfType(componentType, false).OfType<Component>()) 48 { 49 Debug.Log($"{componentType.Name} on {component.name} will be disabled."); 50 disableComponent(component); 51 } 52 } 53}
こうなるともはやアセットの名前しか見ていませんので、セットするオブジェクトがスクリプトファイルである必要もなくなってしまうでしょう。ちょっと気持ち悪いかもしれませんが、下記のように「SphereCollider.txt」という名前のテキストファイルをセットしたところ...
シーン上のSphereが持つSphereColliderが機能を停止し、物理的形状を失ってボールが床を貫通して落下していきました。
投稿2021/06/30 12:06
編集2021/06/30 12:11総合スコア10811
0
回答いただいたMonoScript型を参考にして作成してみました。
targetScriptに無効化したいスクリプトを設定します。
すると OnValidateの中で、targetAssemblyQualifiedName に無効化したいスクリプトのAssemblyQualifiedNameが自動で設定されます。
このAssemblyQualifiedNameを比較して、同じスクリプトを無効化します。
AssemblyQualifiedNameではなくてType型で比較できればよかったのですが、TypeはSerialize出来なかったのでAssemblyQualifiedNameで代替しました。
AssemblyQualifiedNameも使ったことが無いのでこれで完全に問題無いのかよく分かってないですけども、簡易的なテストを行って動作確認済みです。
using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif public class ComponentDisabler : MonoBehaviour { #if UNITY_EDITOR public MonoScript targetScript; #endif [HideInInspector] public string targetAssemblyQualifiedName; void Start() { MonoBehaviour[] monoBehaviours = GameObject.FindObjectsOfType<MonoBehaviour>(); foreach (MonoBehaviour monoBehaviour in monoBehaviours) { if(monoBehaviour.GetType().AssemblyQualifiedName == targetAssemblyQualifiedName) { Debug.Log("Disable "+monoBehaviour.GetType().Name+" on "+monoBehaviour.gameObject.name); monoBehaviour.enabled = false; } } } #if UNITY_EDITOR private void OnValidate() { this.targetAssemblyQualifiedName = this.targetScript.GetClass().AssemblyQualifiedName; } #endif }
投稿2021/07/01 01:10
編集2021/07/01 17:00総合スコア14
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/07/01 01:03