【Unity】transformのpositionを取得して値を変えても反映されないのはなぜ?
初心者の頃に引っ掛かったのが、ゲームオブジェクトのtransform.positionを取得して値を変えても、ゲームオブジェクトに全く変化がない! という点。
transform.positionはVector3型なのですが、このX成分をスクリプトからいじってもシーン内のオブジェクトには反映されないんですよね。
この点は参照型と値型の違いを理解する必要がありました。参照型と値型の違いはC#初心者が悩みがちな所なので、この解説も含めて紹介したいと思います。
環境
macOS 10.15 Catalina
Unity2019.4.4f1
transform.positionを取得して値を変えても反映されない
まずはコードを見てもらいましょうか。
初心者の頃はこれでこのスクリプトをアタッチしたゲームオブジェクトの位置が変わると思っていました。しかし実際にはゲームオブジェクトの位置はそのままに……。
もしゲームオブジェクトの位置を変えたいなら、以下のコードのように取得したposをtransform.positionにセットし直す必要があります。
変数としてposにtransform.positionをセットしたのだから、そのまま参照先の値を変えてくれる……と思っていたのですが、ここに勘違いがありました。実はVector3型はクラスではなくて構造体だったんです。
posに代入した時点ではtransform.positionの値をコピーして渡しているので、posの値をいくら変更してもコピー元のtransform.positionの方は変わらなかったんです。
Vector3は構造体
「構造体って何?」というのは私が初心者だった頃のセリフです。
C#にはクラスと構造体があります。クラスは参照型、構造体は値型となっています。
……なんて、ググってよく見かけるこの説明ですが、プログラミング初心者の頃はこの説明がちんぷんかんぷんで何日か悩み続けた記憶があります。
クラス
クラスというのはオブジェクトの設計図のようなもので、どのようなフィールドを持っていて、どのようなメソッドを持っているかを決めています。この設計図を元に、実際のデータを持ったオブジェクトが生成されています。
クラスから生成されたオブジェクトはメモリ上に情報を持っていますが、例えばなんらかの変数にこのオブジェクトを代入すると、オブジェクトそのもののデータではなくて、オブジェクトへの参照情報がセットされます。参照とは「メモリのここにいます」という情報のこと。
C#ではこうした型のことを参照型と呼んでいます。
構造体
もう一方の構造体もオブジェクトの設計図で、主にデータの持ち方を定義するときに使います。配列やリストだと同じ型の値しか持てませんが、異なる型の値をひとまとめで扱いたい時に構造体を定義すると便利です。
構造体から生成されたオブジェクトもメモリ上に情報を持っていますが、こちらは参照情報ではなくて値そのものを持っています。変数に代入すると分かりやすくて、ある変数に構造体を代入すると、参照情報ではなくて値そのものをコピーして渡します。なので、コピーした先で値を変更したとしても、コピー元の値は変わらないんです。
C#ではこうした型のことを値型と呼んでいます。
よく聞く例え話
参照型と値型の違いを表す話としてよく聞くのが以下のようなもの。
ある時、入社1年目のルーキーUnityエンジニアの流木くんが、プログラミングについて勉強したいと思い立ちました。どんな本が良いか先輩Unityエンジニアの仙波さんに聞いたところ、「それなら僕のロッカーに『リーダブルコード』が入っているよ」と言われました。
仙波さんが参照型だったら、「ロッカーの場所を教えるから本を持ってって使っていいよ」と答えます。場所を教えることによって、流木くんが本を取り出したら先輩のロッカーの中身が変わります。
仙波さんが値型だったら、「同じ本を買ってあげるからこれで勉強してね」と答えます。先輩のロッカーの中身には変化がなく、ロッカーの中にあった本(情報)と同じものが流木くんに渡されます。流木くんが夜遅くまで勉強して、眠気覚ましのコーヒーをドバーッとこぼしてしまったとしても、先輩の本を汚す心配はありません。
このように、変数に代入した時に元の情報を変えるかどうかが参照型と値型のポイントだったりします。
Vector3型は値型なので、変数に代入した値をいじったとしても、コピー元の値は変わりません。今回のケースではtransform.positionが変わらないので、ゲームオブジェクトの位置もそのままになっていました。だから、変更した後のposをセットし直す必要があったんですね。
マニュアルから違いを読み取る
Unity公式のスクリプティングリファレンスでは、型名の側に「class」か「struct」が記載されています。
「class」と書かれている場合はクラスで参照型です。このオブジェクトを変数に代入したら、参照情報が渡されているので値を変更した時にそのままオブジェクトの値が変わります。
「struct」と書かれている場合は構造体で値型です。このオブジェクトを変数に代入したら、その構造体の値がコピーされて渡されるので、元々のオブジェクトの値は変わりません。Vector3型では「struct」と書かれているので構造体だったんです。
クラスと構造体の違いはうっかりしていると見落とすこともあるので、「class」か「struct」の記載をチェックするようにしましょう。
まとめ
transform.positionを取得して値を変えても反映されない点について、初心者の頃に悩んだ気持ちを思い出しながら紹介しました。ポイントは参照型と値型の違いに注目することでした。
ちなみに途中の例え話で挙げた『リーダブルコード』はこんな感じの本です。ガチ初心者が読むにはちょっとハードルが高いかもしれませんが、ある程度開発に慣れてきて綺麗なコードを書きたい時におすすめの本です。
また、今回出てきたVector3型については以下の記事でも触れているのでよかったらご覧くださいな。
ゲーム開発の攻略チャートを作りました!
-
前の記事
各ストアでゲームを公開する費用を比較してみる【ゲーム開発】 2020.11.22
-
次の記事
Unityでよく見るVector3型って何者ね? という時に読む記事 2020.11.24
コメントを書く