[Unity] Destory処理中のOnDisableとOnDestroyの呼び出され方と、その最中にGameObjectの親子関係を解除したときの挙動


結論から

OnDisableやOnDestroyで親子関係を変えると意図しない挙動を引き起こしやすいので

Destroyする前に親子関係を解除した方が良い


検証用GameObjectの構造とコード

コメント 2019-07-09 153613.png


DestroyTest

DestroyTest : MonoBehaviour

{
public bool detachChildrenOnDisable;
public bool detachParentOnDisable;
public bool detachChildrenOnDestroy;
public bool detachParentOnDestroy;

private void OnDisable()
{
if (detachChildrenOnDisable) { transform.DetachChildren(); }
if (detachParentOnDisable) { transform.parent = null; }
Debug.Log(gameObject.name + "OnDisable");
}

private void OnDestroy()
{
if (detachChildrenOnDestroy) { transform.DetachChildren(); }
if (detachParentOnDestroy) { transform.parent = null; }
Debug.Log(gameObject.name + "OnDestroy");

}
}



コードから親をDestroyした場合

親→子→孫という順番でOnDisableが呼ばれ、その後同じ順序でOnDestroyも呼ばれる

コメント 2019-07-09 134746.png


Editorの操作から削除した場合

コメント 2019-07-09 154128.png

Disableは孫→子→親の逆順で呼ばれ、Destroyはコード側の操作と同じ順序で呼ばれる

コメント 2019-07-09 134916.png


親のOnDestroyで親子関係を解除した場合

すべてのOnDisableが呼び出された後、親子関係が解除され親のOnDestroyのみ呼ばれる

1213213.JPG

この場合DestroyしたGameObjectの直下にある子のみdisableのままになり孫はEnableが呼ばれコンポーネントが起動する

1010.JPG

コードにOnEnableを書き加え確認するとしっかり呼ばれているのを確認できる

1111.JPG

Destroy処理の途中で親子関係を外す場合に期待する挙動としては確かに子はDisableされずEnableのままにしてほしいと思うので

中断できた場合は改めてEnableにするのだろうが

構造的に子か否かでその処理が走るかどうか決めていて親になった「子」には適用されないのだろう


親のOnDisableで親子関係を解除した場合

特筆することもなく親だけで処理が完結する

212212.JPG


子のOnDestroyで親を外した時

親を外す処理はDestory中には不可能で、エラーになりそのままDestroyされる

22222222.JPG


子のOnDisableで親を外した時

子でOnDisableの伝播が止まり孫はDisableされていないのでOnEnableも呼ばれない

1515654.JPG

子はDisableのままになる

654566.JPG


Unityが用意しているコンポーネントは?

子がRootへ変化する状況では同じくDisableされる

16548163.JPG

Particle Systemなどは出てるパーティクルが一度すべて消える


感想

あんまり無い状況かもしれませんが、Destory中に親子関係を解除するとコンポーネントがDisableのままというのは

結構罠だと思うので、事前に解除しておきましょう