Unity 5で 2Dシューティングのチュートリアルを行う際の注意点(第10回)
Unity公式チュートリアル 2Dシューティング
第10回 タイトルを付ける
上記チュートリアルで、私がつまずいたところ、初心者がつまずきそうなところをピックアップして解説します。
10.1 タイトルの表示
空のゲームオブジェクトを作成し、名前をTitleとします。Titleゲームオブジェクトの位置はX 0 Y 0 Z 0とします。
メニューから GameObject -> Create Empty を選択し、空のゲームオブジェクトを作成します。
Hierarchyに生成された GameObjectを選択し、F2キーを押して Titleにリネームします。
Inspectorの Transform内にある PositionのXYZを0に編集します。
次に、メニューのGameObject -> Create Other -> GUI Textを選択し、GUIテキストを作成します。名前をShooting Gameとして、Titleの子要素とします。
Unity5では、メニューの GameObjectに Create Otherはありません。
また、GUT Textは、Componentとして呼び出すことができるようです。
そこで、まず、メニューから GameObject -> Create Empty を選択し、空のゲームオブジェクトを作成します。
Hierarchyに生成された GameObjectを選択し、F2キーを押して Shooting Gameにリネームします。
Shooting Gameを Titleにドロップして、Titleの子要素にします。
次に、Shooting Gameを選択し、表示された Inspectorの一番下にある「Add Component」をクリックし、Rendering -> GUI Text を選択します(GUI Textは一番下に隠れています)。
Titleの子要素としたShooting Gameのパラメータを設定していきます。
Shooting Gameの位置はX 0.5 Y 0.65 Z 0とします。
Textを「Shooting Game」、Anchorを「upper center」、FontsフォルダのSAM_5C_27TRG_をFontへ格納、Font Sizeを「60」とします。
Hierarchyタブの Title配下にある Shooting Gameを選択し、表示されたInspectorから、上記の通りパラメータを設定します。
すると、Unity5では、以下のようにちょっと文字が大きめになりました。
ちょうどチュートリアルと同じくらいの大きさにするには、Font Sizeを「47」くらいにすると良いようです。
以下は、47にしてみたところです。
次にShooting GameゲームオブジェクトをDuplicateして、名前をPress Xとします。
Shooting Gameゲームオブジェクトと変更する部分は位置のYを0.45、Textを「Press X」になります。
Hierarchyタブの Shooting Gameを右クリックし、Duplicateを選択します。
複製されたものを選択し、F2キーを押して「Press X」にリネームします。
表示されているInspectorで、上記の通りにY位置とTextを修正します。
10.2 タイトル -> ゲームスタート ( -> 死んだら ) -> タイトル のマネージャークラスを作る
タイトル表示中はプレイヤーやWaveの作成を行わないようにします。
Playerゲームオブジェクトの削除
まずはPlayerの位置を X 0 Y -2.5 Z 0にしてプレハブを更新して、Playerゲームオブジェクトを削除してください。
Hierarchyタブの Playerを選択し、Inspectorの PositionでXYZ位置を修正します。
その後、Inspectorの Prefab横の「Apply」を押して、プレハブを更新します。
最後に、Hierarchyタブの Playerを選択し、Deleteキーを押して削除します。
Managerの作成
空のゲームオブジェクトを作成し、名前をManagerとします。
次にManager.csをScriptsフォルダの中に作成します。
メニューから GameObject -> Create Empty を選択し、空のゲームオブジェクトを作成します。
Hierarchyに生成された GameObjectを選択し、F2キーを押して Managerにリネームします。
次に、ProjectタブのScriptsを右クリックし、Create -> C# Scriptを選択後、追加された NewBehaviourScriptを Managerにリネームします。
リネームした Managerをダブルクリックし、MonoDevelopで開いて、Manager.csを編集します。
Manager.csのソースは、チュートリアルの内容でまるごと置き換えて問題ありません。
置換後、Ctrl+Sで保存し、Unity画面に戻りましょう。
もし NullReferenceException が発生してしまったら?
スクリプトの実行順によっては Manager.cs で参照するはずの title を取得する前に Emitter.cs などで title にアクセスしようとしてしまいます。この時に NullReferenceException が発生してしまうかもしれません。この解決方法としてスクリプトの実行順を管理する「Script Execution Order」を利用します。
ここに書いてある現象が発生すると、ゲームを開始しても敵が流れてこなくなります。
デバッグするとわかるのですが、書かれている通り、NullReferenceException で落ちます。
落ちる、といっても内部でこっそりオブジェクトが死ぬだけで、ゲーム自体は動き続けます。
正確に言うと、Emitterオブジェクトが内部で落ちて消滅し、その後、Waveを発生させることができなくなります。
なお、現時点でこの現象が起きていなくても、このコラムの内容は必ず実行しておいてください。
Unityを保存し、別の日に起動した時などに突然現象が発生するようになり、パニックになります。
#私はなりました……
落ちる時の流れとしては、
Emitter.cs の Start()内にある whileループの中の以下の箇所で Manager.IsPlayng()をコールしますが
while(manager.IsPlaying() == false) {
その中の以下でtitleを参照している箇所で落ちます。このとき、title == null になっているためです。
public bool IsPlaying () { // ゲーム中かどうかはタイトルの表示/非表示で判断する return title.activeSelf == false; }
Manager.cs で titleを初期設定するのは Manager.Start ()なのですが、ここが呼ばれる前に Emitterから IsPlayngが呼び出されてしまうために、null参照による例外が発生し、落ちてしまうのです。
このため、コラムに書いてある「Script Execution Order」で Scriptの実行順を指定し、落ちないようにする必要があります。
あるいは、以下のようにManager.IsPlayng()を書き換えても、一応動作はするようになります。
事前にnullチェックを行って、条件文では落ちないようにしているだけです。
return (title != null && title.activeSelf == false);
では、コラムの通りに Scriptの実行順を設定してみましょう。
メニューの Edit -> Project Settings -> Script Execution Order を選択することで MonoManager がインスペクターに表示されます。元々スクリプトの実行順は保証されているわけではなく、ユーザーは実行順を把握することは出来ません。その状態が「Default Time」と表記されている部分です。
MonoManagerでは「Default Time」から特定のスクリプトを抜き出して実行タイミングを指定することが出来ます。
「+」ボタンを押して Manager を選択してみましょう。
ここは書かれているとおりにできると思います。
すると、Managerの実行タイミングを表すフィールドが追加されるので、そのフィールドを摘んで「Default Time」の上に移動させます。
ここはちょっとわかりにくいかもしれませんが、追加された「Manager」の行は、上下にドラッグすることができます。
#左側に「つまんで動かせるよ」的な「=」マークがあるのが見えると思います。
「Manager」の文字の当たりでマウスボタンを押下して、上にドラッグすると、「Manager」と「Default Time」の位置を入れ替えることができます。
位置を入れ替えたら、すぐ下にある「Apply」ボタンを押して、変更を適用してください。
これで全スクリプトで一番最初に実行されるのは Manager.cs となりました。
これにより、Managerスクリプトを最も最初に実行することが設定できたことになります。
マネージャーにPlayerプレハブの設定
ここのチュートリアルの画像を見る限り、HierarchyタブのManagerゲームオブジェクトにManagerスクリプトがアタッチされていますので、まずはそれを行います。
HierarchyタブのManagerを選択し、Inspector内の「Add Component」をクリック、Scripts -> Manager を選択します。
すると、Inspectorに Manager(Script)が追加されるので、そこの Player枠に、ProjectタブのPrefabs配下にある Playerをドラッグ&ドロップします。
10.3 他のスクリプトからマネージャーを呼び出す
Player.csを修正します。
ProjectタブのScripts配下にある Playerをダブルクリックし、MonoDevelopで Player.csを編集します。
Player.csのソースは、チュートリアルの内容でまるごと置き換えて問題ありません。
置換後、Ctrl+Sで保存して、Unity画面に戻りましょう。
ダイアログが表示されるので、「Go Ahead!」をクリックし、ソースを自動修正させましょう。
Player.cs の変更点は以下の通りです。
・OnTriggerEnter2D関数内に、Manager.csの GameOverメソッドを呼び出す処理を追加
Emitter.csを修正します。
ProjectタブのScripts配下にある Emitterをダブルクリックし、MonoDevelopで Emitter.csを編集します。
Emitter.csのソースは、チュートリアルの内容でまるごと置き換えて問題ありません。
置換後、Ctrl+Sで保存して、Unity画面に戻りましょう。
Manager.cs の変更点は以下の通りです。
・Managerコンポーネントを格納する変数 managerを追加
・Start関数に、managerの初期化コードを追加
・Start関数のwhileループ内に、タイトル表示中は待機(yield return)するコードを追加
さて、ゲームを再生してみてください。タイトル表示中はプレイヤーも敵も出てませんか?Xキーを押すとゲームが開始されますか?
ここまでうまくできていれば、上記の通りに動作するはずです。
今回はこれにて終了です。
うまくいきましたか?