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

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

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

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

解決済

2回答

1286閲覧

UnityEngineライブラリクラスのプライベートフィールド名を確認してGatherPropertiesで保護したい

yuraoka

総合スコア3

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

0クリップ

投稿2020/07/09 04:32

GatherProperties機能の確認

UnityのタイムライントラックにはGatherPropertiesという仮想メソッドがあり、
これをオーバーライドしてタイムラインプレビュー中に変更した値を元に戻すのが一般的かと思います。
公式ドキュメント

コンポーネントのフィールドの保護

コンポーネントのプロパティに対しては以下のメソッドで値を保護することができています。

// driverはIPropertyCollectorインターフェースを実装したオブジェクトです。 driver.AddFromName <コンポーネントの型>(対象のゲームオブジェクト, シリアライズフィールド名);

ゲームオブジェクトのフィールドの保護

同様にゲームオブジェクトのフィールド、特にActiveSelfプロパティのフィールドを保護しようとしたのですが、うまくいきませんでした。

driver.AddFromName( 対象のゲームオブジェクト, "m_ActiveSelf");

エラーメッセージ

DrivenPropertyManager has failed to register property "m_ActiveSelf" of object "TsetObject" with driver "" because the property doesn't exist.

課題1:ライブラリクラスのプライベートフィールド名を調べたい

今ある課題としては、フィールド名がm_ActiveSelfで合っている保証がありません。UnityEngineライブラリクラスのプライベートフィールドなので確認する方法がないためです。
試しに以下のようにリフレクションで取得しようとしてもうまくいきませんでした。

var fieldNames = typeof(GameObject).GetFields(System.Reflection.BindingFlags.NonPublic).Select(field => field.Name).ToArray(); Debug.Log(fieldNames.Length); // 0

例えばトランスフォームであればm_LocalPositionというフィールドがありますが、このようなプライベートフィールド名を知る一般的な方法はどのようなものなのでしょうか。

課題2:ゲームオブジェクトのフィールドを保護したい

ライブラリクラスのプライベートフィールドの確認方法を理解した上で上記と同じ

driver.AddFromName( 対象のゲームオブジェクト, シリアライズフィールド名);

でゲームオブジェクトのActive状態を保護できるのか試したいと考えています。
もしできなければ、ゲームオブジェクトはコンポーネントとは違って特殊なのか、それともActiveプロパティが特殊なのか切り分けて行きたいと考えています。

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

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

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

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

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

guest

回答2

0

ベストアンサー

bboydaisukeさんのおっしゃる通り、Unityユーザーが自由に使えるようにpublicとして公開している、マニュアルやリファレンスでちゃんと解説されている機能だけを使って何とかするのが王道かと思います。裏技の範疇に足を突っ込むのであれば、たとえばバージョンアップで予告なしに動かなくなっても文句は言えない...みたいなリスクも覚悟する必要があるでしょう。

アセンブリをデコンパイルしてみるほかにも、GitHub - Unity-Technologies/UnityCsReference: Unity C# reference source codeを読んでみても発見があるかもしれません(参考情報として公開されているだけのようで、いわゆる「オープンソース」というわけではないみたいですからご注意ください)。
たとえばUnityCsReference/GameObject.bindings.cs at master · Unity-Technologies/UnityCsReference · GitHubには、見慣れたメソッドやらプロパティやらが並んでいるのを見て取れるかと思います。
中ほどには...

C#

1 public extern bool activeSelf 2 { 3 [NativeMethod(Name = "IsSelfActive")] 4 get; 5 }

なんて記述があり、アクティブかどうかはC#上のフィールドとしてではなく、ネイティブコード側で管理しているみたいですね。

とはいえ、シーン上にゲームオブジェクトを配置してアクティブ状態を切り替えて保存すれば、次回Unityを起動したときにもちゃんとアクティブ状態が復元されます。ですので、何かしらの情報をシリアライズしているであろうことは想像されるでしょう。
シリアライゼーションに関しては、マニュアルのテキストシーン形式の節もお役に立つかもしれません。Format of Text Serialized filesには、下記のような例が載っていました。

YAML

1--- !u!1 &6 2GameObject: 3 m_ObjectHideFlags: 0 4 m_PrefabParentObject: {fileID: 0} 5 m_PrefabInternal: {fileID: 0} 6 importerVersion: 3 7 m_Component: 8 - 4: {fileID: 8} 9 - 33: {fileID: 12} 10 - 65: {fileID: 13} 11 - 23: {fileID: 11} 12 m_Layer: 0 13 m_Name: Cube 14 m_TagString: Untagged 15 m_Icon: {fileID: 0} 16 m_NavMeshLayer: 0 17 m_StaticEditorFlags: 0 18 m_IsActive: 1

いかにも怪しげなm_IsActiveなんてプロパティが見えますが、残念ながらこれが何を意味するのかまでは解説されていないようです。

ほかにも取っかかりになりそうな所として、ActivationTrackはまさしくゲームオブジェクトのアクティブ状態を切り替える機能を持っています。そこでプロジェクトのフォルダ内をActivationTrackで全文検索してみたところ...

C#

1using System; 2using UnityEngine.Playables; 3 4namespace UnityEngine.Timeline 5{ 6 /// <summary> 7 /// Track that can be used to control the active state of a GameObject. 8 /// </summary> 9 [Serializable] 10 [TrackClipType(typeof(ActivationPlayableAsset))] 11 [TrackBindingType(typeof(GameObject))] 12 [ExcludeFromPreset] 13 public class ActivationTrack : TrackAsset 14 { 15 [SerializeField] 16 PostPlaybackState m_PostPlaybackState = PostPlaybackState.LeaveAsIs; 17 ActivationMixerPlayable m_ActivationMixer; 18 19 /// <summary> 20 /// Specify what state to leave the GameObject in after the Timeline has finished playing. 21 /// </summary> 22 public enum PostPlaybackState 23 { 24 /// <summary> 25 /// Set the GameObject to active. 26 /// </summary> 27 Active, 28 29 /// <summary> 30 /// Set the GameObject to Inactive. 31 /// </summary> 32 Inactive, 33 34 /// <summary> 35 /// Revert the GameObject to the state in was in before the Timeline was playing. 36 /// </summary> 37 Revert, 38 39 /// <summary> 40 /// Leave the GameObject in the state it was when the Timeline was stopped. 41 /// </summary> 42 LeaveAsIs 43 } 44 45 internal override bool CanCompileClips() 46 { 47 return !hasClips || base.CanCompileClips(); 48 } 49 50 /// <summary> 51 /// Specifies what state to leave the GameObject in after the Timeline has finished playing. 52 /// </summary> 53 public PostPlaybackState postPlaybackState 54 { 55 get { return m_PostPlaybackState; } 56 set { m_PostPlaybackState = value; UpdateTrackMode(); } 57 } 58 59 /// <inheritdoc/> 60 public override Playable CreateTrackMixer(PlayableGraph graph, GameObject go, int inputCount) 61 { 62 var mixer = ActivationMixerPlayable.Create(graph, inputCount); 63 m_ActivationMixer = mixer.GetBehaviour(); 64 65 UpdateTrackMode(); 66 67 return mixer; 68 } 69 70 internal void UpdateTrackMode() 71 { 72 if (m_ActivationMixer != null) 73 m_ActivationMixer.postPlaybackState = m_PostPlaybackState; 74 } 75 76 /// <inheritdoc/> 77 public override void GatherProperties(PlayableDirector director, IPropertyCollector driver) 78 { 79 var gameObject = GetGameObjectBinding(director); 80 if (gameObject != null) 81 { 82 driver.AddFromName(gameObject, "m_IsActive"); 83 } 84 } 85 86 /// <inheritdoc/> 87 protected override void OnCreateClip(TimelineClip clip) 88 { 89 clip.displayName = "Active"; 90 base.OnCreateClip(clip); 91 } 92 } 93}

なんて記述が見つかりました。これはけっこうな手がかりになるんじゃないでしょうか?
ですが、やはりbboydaisukeさんの「すでに一般的ではない」という警告はもっともだと思いますので、この方針で進めるかどうかは熟慮いただくのがいいかと思います。

投稿2020/07/09 11:56

Bongo

総合スコア10807

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

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

yuraoka

2020/07/09 14:00

ご回答ありがとうございます。 GitHubのリンクと参考コードの引用までありがとうございます! ヒントを元に試したら目的の動作はできたのですが、やはり裏技感は否めなかったです。 正攻法で実装するのであれば以下の2パターンでしょうか。 正攻法?1: Unityクラスのプロパティを操作したい場合はそれ用のコンポーネントを追加する ``` public sealed class SerializableActiveSelf : MonoBehaviour { [SerializeField] bool activeSelf = true; private void OnValidate() { gameObject.SetActive(activeSelf); } } ``` OnValidateがエディタ以外で動いたかはうろ覚えです。 Updateで値の変更を監視して反映でもよいかもしれません。 正攻法?2: PlayableBehaviourの`OnGraphStart`と`OnGraphStop`をオーバーライドしてキャッシュと復元を行う ``` private bool targetActiveCache; public override void OnGraphStart(Playable playable) { this.targetActiveCache = target.activeSelf; } public override void OnGraphStop(Playable playable) { target.SetActive(targetActiveCache); } ```
Bongo

2020/07/09 15:28

そうですね、おっしゃるように仲介役のスクリプトを用意するのは妥当な方法かと思います。ですが、回答を投稿しておきながら申し訳ないですけれども、私はまだタイムラインを頻繁に使用した経験がなくてコメントいただいた方法で問題ないかどうか判断しかねるところがあります... OnValidateについては、確かにリファレンス(https://docs.unity3d.com/ja/current/ScriptReference/MonoBehaviour.OnValidate.html )を見てみたところエディター上でしか機能しないようです。また、PlayableGraphのスタート・ストップタイミングで処理するという案についても、PlayableGraphが動いている途中でゲームオブジェクトへの参照を付け外ししたりするような状況には対応できないのではないか、といった懸念もありますね... ですが少なくとも前者のOnValidate案であれば、エディター上での動作に問題がなければ実機でのゲームプレイ時に動作しないとしても大したことはないんじゃないかと思います。制作者がエディター上で任意のタイミングでタイムラインを編集する可能性がある状況とは異なり、実際のプレイ時にはすべての動作がプログラマーの記述したコードに従って行われることになるでしょう。ですのでゲームオブジェクトへの参照を操作するタイミングもプログラマーが把握できているはずであり、そのタイミングで適切にアクティブ状態を切り替えてやればいいような気がします。
yuraoka

2020/07/10 14:38

なるほどOnGraphStartとStopでは参照変更の可能性がありましたね・・・ OnValidateはタイムラインからシリアライズフィールドを操作した際には反応しなかったです・・・ (インスペクタからの操作ではもちろん反応しました。) [ExecuteInEditMode]を付けてUpdateしてみても、GameObjectが非アクティブになるのでUpdateが止まってしまいました。 自身は非アクティブにならないオブジェクトに[ExecuteInEditMode]を付けて、 実際にSetActiveしたいオブジェクトへの参照を持つという方法はありそうですが回りくどい・・・ 元々の目的としてはオブジェクトのActive状態を特定のパターンで切り替えるタイムラインを作りたかったからなので、 ・目的のパターンのActivationトラックを自動生成する機能を追加する ・Rendererの方をdisableにするなどで表示を消す。 という方法もあるかもです。
guest

0

このようなプライベートフィールド名を知る一般的な方法はどのようなものなのでしょうか。

隠蔽している情報を知ろうということでやろうとしていることがすでに一般的ではないように思いますが、方法としては mono のアセンブリを逆アセンブルする、ということかなと思います。
やり方は書かないので調べて欲しいのですが、結果としては activeSelf は別にプライベートフィールドの値を返しているわけではないので「ActiveSelfプロパティのフィールドを保護」ということはできない、という結論になると思います。

投稿2020/07/09 08:14

bboydaisuke

総合スコア5275

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

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

yuraoka

2020/07/09 14:00

ご回答ありがとうございます。 逆アセンブリして得たフィールド名を使って無理やり値を操作というのはあまりよい方法ではなさそうですね・・・ Unity公式で公開/推奨している情報があれば使用したかったのですが、可能な限り公開されているプロパティを操作する方法で実装していきたいと思います。
bboydaisuke

2020/07/09 14:02

> 逆アセンブリして得たフィールド名を使って そういうことではなくて、上に書いたようにフィールドを返しているわけではなく、フィールド自体ありません。
yuraoka

2020/07/09 14:08 編集

すみませんactiveSelfについてはシリアライズフィールドで実装されているわけではないとのことでしたね。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問