Unityのここがわかりにくい!~スクリプトはどこから実行されている?基礎編~
Unityの謎の壁 第一 スクリプトの実行順序と本稿の対象読者
Unityを数時間、数十時間ぐらい触った事がある人ならわかると思いますが、Unityには【謎の壁】があります。
この謎の壁、なんと挫折率90%だそうです。
そんな謎の壁を言語化し、その壁を越える為のワンステップが20cmぐらいの階段を作り、だれでもその壁を乗り越えられるようにしようという本稿の目的になります。
対象者:少しUnityを触ったけど、スクリプトが出てきて、挫折してしまったまたは挫折しそうな人。
謎の壁四天王の最初にして最凶が、「スクリプト実行順序」です。
Unityでは、スクリプトがどこからどこまでどういう順番で実行されてるのか分かりにくいのです。
JavaやCのように、Mainから実行して、全てそこからたどれないのか?
Perl、PHP、HTMLのように、単にファイルの上順番に実行しないの?
Unityでは、謎の詠唱から入って、間間にプツ切れになったスクリプトが胡散した状態で実行されています。
だから、何をどこにどうやって書けば、どんな処理をさせる事がイメージできずに、制作が着手できない。
これは、Unityエンジンの開発者が頭が悪いから…そうなっているわけではなく、設計思想に基づく事で、ある意味しょうがない事でもあるんです。その話は別の機会にしましょう。
今までの経験は捨てて…
魔法の言葉を覚えましょう。
その魔法の第一弾は…
エントリーポイント(EP)メソッド!
EPメソッドを知っていきましょう!
エントリーポイントメソッドとは?
エントリーポイントとは、開始地点の事です。
つまり、Unityエンジンの中で、スクリプトの開始地点になりえるメソッドです。
少しでもUnityのスクリプトを見たことがあるなら、目にした事があるであろう以下の二つは、最も使われるEPメソッドです。
void Start()
簡易説明: Updateが最初に呼びされる前に一度だけ実行する
void Update()
簡易説明: 毎フレーム実行する
(これらの説明は最低限の理解の為の簡易的なものなので丸暗記しない方がいいです)
このようなEPメソッドは、一つのプロジェクトの中で、一つだけではなく、スクリプトの数だけ…それはもう大量に存在します。
あなたが自身が、EPメソッドを自分のスクリプト内で記述しなくても、Unity Asset Storeから拾ってきたスクリプトや、Githubから拾ってきたスクリプト、Unityの標準のコンポーネントである Image、RigidBody、Buttonなどと言ったスクリプトもこれらのEPメソッドを記述して、作られています。
それらのスクリプトをEditor画面でゲームオブジェクトに付与した瞬間に、それらのスクリプトに含まれているEPメソッドが登録され、実行対象になっています。
そう、Unity上で実行されている以上は、例外はなく、
必ずEPメソッドを通じてのみ、スクリプトは実行されます。
直列に実行されているEPメソッド
さて、エンジン上での同フレーム処理内でも、一つのEPメソッドが実行されると、次のEPメソッドが実行されていくというように直列で実行されています。
仮に、「実行順序テスト.cs」(後にソースコードがあります)
というスクリプトにStartとUpdateを記述したとして、それらをキューブAとキューブBに付与した場合の実行されるイメージは以下の通り。
(キューブAをキューブBより上にHierachy上で配置している場合)
ーー最初のフレームーー
キューブAの「実行順序テスト.cs」Startが実行される
↓
キューブBの「実行順序テスト.cs」Startが実行される
↓
キューブAの「実行順序テスト.cs」Updateが実行される
↓
キューブBの「実行順序テスト.cs」Updateが実行される
ーー2フレーム以降ーー
キューブAの「実行順序テスト.cs」Updateが実行される
↓
キューブBの「実行順序テスト.cs」Updateが実行される
ーー2フレーム以降をループーー
となります。
実際に、キューブなどを動かしたりするプログラムを作っても、実行順序は分かりずらいので、実行順序を学んでいる時には、全てDebug.Logでログ表示だけを取る事をお勧めします。
EPメソッドの書き方
上記の実行順序テスト.csの中身です。
using UnityEngine;
public class 実行順序テスト: MonoBehaviour{
void Start(){
Debug.Log(gameObject.name + "のStartが実行されました");
}
void Update(){
Debug.Log(gameObject.name + "のUpdateが実行されました");
}
}
説明していきます。
上記のMonoBehaviorというのは、UnityEngineがEPメソッドを記述して意味が発生するエンジンで定義されているクラスです。using UnityEngineは、このMonoBehaviourを利用する為の宣言になります。
まず、このUnityエンジン様がお決めになった
「MonoBehaviour」をこのように継承します。
継承というのは、このように定義したいクラス名の : の後に名前を記述することで親クラスを設定でき、この新しいクラスの特性として親の特性を受け継がせる事です。
受け継がれたスクリプトの内容は表示されませんので実感できませんが、MonoBehaviourを継承しただけで、あなたのクラスは、非常に多機能なクラスになっています。
このMonoBehaviour君は、非常に多機能で、Unity開発での最重要人物ですが、何をしているのか報告や連絡や相談をしてくれない…
つまり、報連相が出来ないヤツです。
コイツのせいで、Unityの挫折率が90%であると言っても過言ではないでしょう。
はぁはぁ…
兎に角、これを記述する事で、Unityエンジン様にEPメソッドを認知してもらえる…というシンプルな理解で90%問題ありません。
次に、MonoBehaviourの後の 一つ目の { } で囲んで範囲を指定している中で、void Start(){やvoid Update()などの「お決まりのEPメソッド」で記述すると、自動的にUnityエンジンに実行対象になります。
もし他のEPメソッドにも興味あれば、下記の「メッセージ」を参照してください。それぞれのEPメソッドが実行される条件が日本語で書かれています。
https://docs.unity3d.com/ja/2021.1/ScriptReference/MonoBehaviour.html
これらすべてを正確に覚えておく必要はありませんが、軽く目を通して、おおよそどんなEPメソッドが存在しているかを確認しておくと、自分がイメージしているゲームに必要なEPメソッドがどれであるべきか判断が付きます。
気になったEPメソッドを見かけたら、先の「実行順序テスト」にEPメソッドを追加していき、実行内容にDebug.Logだけを記述して、実行される条件を検証していくのを自分の目で見るのが最も勉強になります。
EPメソッド以外のメソッドは何の為にある?
では、EPメソッド以外の自分や誰かが書いたメソッドはなんの為にあるのでしょうか?
EP以外のメソッドには、Unityエンジン的にはなんの意味もありません。
スクリプトを読みやすくしたり、再利用性を高める為だけに、開発者が都合よく勝手に使ってるだけです。
つまり、開発者が好きなように自分や読む人の為に都合よく使ったらいいです。
そういう話しは、他のプログラミング言語でも共通の基礎であり、Unityの壁とは関係ないので投稿ではこれ以上は言及しません。
以上