質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

2910閲覧

Cast<Transform>()は何をしているか。

退会済みユーザー

退会済みユーザー

総合スコア0

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

1グッド

0クリップ

投稿2018/11/24 16:38

前提・実現したいこと

子オブジェクトの一括削除を調べていて、こちらのサイトを見たのですが、

C#

1 go.transform.Cast<Transform>().ToList().ForEach(x => { x.parent = null; GameObject.Destroy(x.gameObject);}

このコードのCast<Transform>()って何をしているかご教示いただけませんか?

試したこと

キャストが型変換なのはわかっています。
例えば、int型の数をキャストして文字列に変換できるという使い方があることを知っています。

ただ、「transform.Cast<Transform>()」は、「transformをtransformにキャスト?」しているのでしょうか?
「何をしているかわかっていない」ので、「何故、この処理が必要か」ということもわかりません。

また、「unity cast」で検索すると、Collider2Dのキャストしか引っ掛かってこないのですが、Cast<Transform>()のCastのリファレンスってあるのでしょうか?

余談ですが、ForEach内の「x.parent = null;」の処理って必要ですか?
ゲームを実行したら、なくても子オブジェクトが破棄できているような挙動をしました。

Hawn👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

リファレンスには...

また、以下のようにループを使って子の Transform を使用する列挙体をサポートしています。

としか書かれていませんが、Transformは...

C#

1using System; 2using System.Collections; 3using System.Runtime.CompilerServices; 4using UnityEngine.Bindings; 5using UnityEngine.Internal; 6using UnityEngine.Scripting; 7 8namespace UnityEngine 9{ 10 /// <summary> 11 /// <para>Position, rotation and scale of an object.</para> 12 /// </summary> 13 [NativeHeader("Configuration/UnityConfigure.h")] 14 [NativeHeader("Runtime/Transform/Transform.h")] 15 [NativeHeader("Runtime/Transform/ScriptBindings/TransformScriptBindings.h")] 16 [RequiredByNativeCode] 17 public class Transform : Component, IEnumerable 18 { 19 // 省略 20 21 public IEnumerator GetEnumerator() 22 { 23 return (IEnumerator) new Transform.Enumerator(this); 24 } 25 26 // 省略 27 28 private class Enumerator : IEnumerator 29 { 30 private int currentIndex = -1; 31 private Transform outer; 32 33 public object Current 34 { 35 get 36 { 37 return (object) this.outer.GetChild(this.currentIndex); 38 } 39 } 40 41 internal Enumerator(Transform outer) 42 { 43 this.outer = outer; 44 } 45 46 public bool MoveNext() 47 { 48 return ++this.currentIndex < this.outer.childCount; 49 } 50 51 public void Reset() 52 { 53 this.currentIndex = -1; 54 } 55 } 56 } 57} 58

となっており、IEnumerableインターフェイスを実装しています。
ただしこれはジェネリック版のIEnumerable<Transform>ではないため、そのままではLINQの各種メソッドが使えません。
そこでCast<Transform>を使うとIEnumerable<Transform>に変わり、LINQが使えるようになります。もしここで、何らかの条件によって削除するTransformをふるい分けたい...などといった場合には、この後にLINQのメソッドをつなげてやることができるでしょう。
さらにToListList<Transform>に変えることで、ForEachが使えるようになります。

x.parent = null;については私も最初は不必要じゃないかと思いましたが、ちょっと違いの出てくる状況を思いつきました。
下記のようなコードで、親の切り離しがある場合とない場合を比較してみると...

C#

1using System.Collections; 2using System.Linq; 3using UnityEngine; 4 5public class DeleteChildrenTest : MonoBehaviour 6{ 7 public GameObject TargetA; 8 public GameObject TargetB; 9 10 private IEnumerator Start() 11 { 12 // x.parent = null;がある場合 13 Debug.Log("--------Detach parent--------"); 14 Debug.LogFormat("Child count:{0}", this.TargetA.transform.childCount); 15 Debug.Log("Children:"); 16 foreach (Transform t in this.TargetA.transform) 17 { 18 Debug.LogFormat("\t{0}", t.name); 19 } 20 Debug.Log("Destroy!"); 21 UnityUtil.DestroyChildren_LinQ(this.TargetA); 22 Debug.LogFormat("Child count:{0}", this.TargetA.transform.childCount); 23 Debug.Log("Children:"); 24 foreach (Transform t in this.TargetA.transform) 25 { 26 Debug.LogFormat("\t{0}", t.name); 27 } 28 29 // x.parent = null;がない場合 30 Debug.Log("--------Keep parent--------"); 31 Debug.LogFormat("Child count:{0}", this.TargetB.transform.childCount); 32 Debug.Log("Children:"); 33 foreach (Transform t in this.TargetB.transform) 34 { 35 Debug.LogFormat("\t{0}", t.name); 36 } 37 Debug.Log("Destroy!"); 38 DestroyChildrenKeepParent(this.TargetB); 39 Debug.LogFormat("Child count:{0}", this.TargetB.transform.childCount); 40 Debug.Log("Children:"); 41 foreach (Transform t in this.TargetB.transform) 42 { 43 Debug.LogFormat("\t{0}", t.name); 44 } 45 46 // 1フレーム待って... 47 yield return null; 48 49 // x.parent = null;がある場合 50 Debug.Log("--------Detach parent--------"); 51 Debug.LogFormat("Child count:{0}", this.TargetA.transform.childCount); 52 Debug.Log("Children:"); 53 foreach (Transform t in this.TargetA.transform) 54 { 55 Debug.LogFormat("\t{0}", t.name); 56 } 57 58 // x.parent = null;がない場合 59 Debug.Log("--------Keep parent--------"); 60 Debug.LogFormat("Child count:{0}", this.TargetB.transform.childCount); 61 Debug.Log("Children:"); 62 foreach (Transform t in this.TargetB.transform) 63 { 64 Debug.LogFormat("\t{0}", t.name); 65 } 66 } 67 68 private static void DestroyChildrenKeepParent(GameObject go) 69 { 70 71 if (go.transform.childCount > 0) 72 { 73 go.transform.Cast<Transform>().ToList().ForEach(x => { GameObject.Destroy(x.gameObject); }); 74 } 75 } 76}

親を切り離さない場合は、1フレーム経ってオブジェクトが削除されるまでは、子のDestroy後でも子を列挙することができました。

コンソール

投稿2018/11/24 20:29

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2018/11/26 15:22

ご回答ありがとうございます。 ご提示のドキュメントを拝見しました。 「A.Cast<b>()とすると、AがIEnumerable<b>型に変換される。AがCastを呼び出すには、Aの基底クラスがEnumerableである必要がある。」という認識で合っていますか? なるほど、そのフレーム内ならば列挙可能ということですね。 Destroy後に、そのフレーム内で取得しようとしない限りは全く問題なさそうですね。 Destroyの次の直後にInstantiateで別のオブジェクトを子として吊るすという処理をしてみましたが、問題なく切り替わっているように見えました。 ただ、万全を期すならば、子オブジェクトは親の切り離しをしてから破棄すると覚えたほうがよいみたいですね。
Bongo

2018/11/26 22:22

だいたいそのような感じでいいと思いますが、「Aの基底クラスがEnumerableである必要がある」という部分に関しては、厳密さを求める人でしたら「『クラスの継承』と『インターフェイスの実装』は区別せよ」と言うかもしれません(参考1)。「AがIEnumerableを実装している必要がある」の方がよさそうな気がします。 実際のところ、Enumerableクラス(参考2)というのはLINQ用のさまざまな拡張メソッド(参考3)を提供するための静的クラス(参考4)でして、Enumerableクラス自体を継承して独自のクラスを作ろうとしてもエラーが出るかと思います。 参考1: https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/interfaces/ 参考2: https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable?view=netframework-4.7.2 参考3: https://ufcpp.net/study/csharp/sp3_extension.html 参考4: https://docs.microsoft.com/ja-jp/dotnet/csharp/programming-guide/classes-and-structs/static-classes-and-static-class-members
退会済みユーザー

退会済みユーザー

2018/11/27 14:39

ご回答ありがとうございます。 ご提示いただいたURL全て拝見しました。 「拡張メソッド」という言葉は見覚えがありましたが、意味を勘違いしていたので、ご提示いただいたURLでとても勉強になりました。 また、ご指摘ありがとうございます。 「A.Cast<b>()とすると、AがIEnumerable<b>型に変換される。AがCastを呼び出すには、AがIEnumerableを実装している必要がある。AがIEnumerable<b>型に変換されることによって、LINQが使えるようになる」という一連の流れを理解することができました。 ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問