20210908のUnityに関する記事は3件です。

Unity スクリプトの実行順番をコントロールする方法

0.0 はじめに Unityでは異なるスクリプト間でのAwake/Startメッソドの実行順序は原則不規則です。この順番を制御する方法があります。 下記のどちらかで制御が可能です。 1.0 [DefaultExecutionOrder()]属性をつかう 下のように[DefaultExecutionOrder()]属性をクラス(スクリプト)につけます。()内に数字をいれます。小さな数字から実行されます。0がデフォルトなので先に実行させるためにマイナスの数字をいれると良いでしょう。 Test.cs [DefaultExecutionOrder(-5)] public class Test : MonoBehaviour{ } 2.0 Script Execution Orderをつかう メニューで Edit --> Project Settings(Unity2018.4)を選びます。 『Script Execution Order』を選択します。 ウィンドウでの設定はシンプルで、[+]を押してスクリプトを追加し、スクリプトを実行したい順に並べるだけです。数字が小さなものから(上から順に)実行されます。全てのスクリプトを指定する必要はありません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Unity で、気軽に呼び出せるダイアログを作る

Unity で、気軽に呼び出せるダイアログを作る。 Unity でダイアログを作る方法はすでにいろいろ紹介されていますが、ここでは「気軽に呼び出せる」をコンセプトにしたダイアログを作ってみます。 気軽に呼び出せるとは、「Debug.Log」とか、JavaScript の alert のように使え、ヒエラルキーにオブジェクトを置いておく必要もなく、従って、Scene を気にせずにどこからでも呼び出せるものを目指します。 仕様 ダイアログの呼び出し方は、とりあえず以下の3種類を用意します。 /* メッセージとOKボタンを出し、OKボタンを押すと消える */ MyDialog.Alert("メッセージ"); /* メッセージとOKボタンを出し、OKボタンを押すと指定した処理を実行する */ MyDialog.Alert("メッセージ", Click_OK); /* メッセージとOKボタンとCanselを出し、ボタンを押すと指定した処理を実行する */ MyDialog.Confirm("メッセージ", Click_OK, Click_Cancel); /* 上記呼び出しで呼び出されるAction */ void Click_OK() { ... なんらかの処理 } void Click_Cancel() { ... なんらかの処理 } Unity 2020.2.1f1 と、2019.3.15f1 で動作確認しました。 実装方法 上記のように呼び出せるようにするため、スクリプトは MonoBehaviour を継承せず、スクリプト内部で GameObjectを生成するようにします。これは、まずは GameObject を用意してスクリプトをアタッチするUnityでの一般的な作りとは、反対方向のアプローチになります。 ただし、Canvas と EventSystem までスクリプトで用意するのは面倒なので、この2つだけはヒエラルキーで作って下さい。ダイアログを表示したい全ての Scene で、Canvas と EventSystem を用意しておく必要があります。 ヒエラルキーに Canvas と EventSystem を作るには、UI - Button などを作成してから削除するのが楽です。 本当に何も考えずにスクリプトだけで GameObject を作ると、フォントサイズとかのプロパティを全てスクリプトで指定することになって面倒なため、プレハブを使います。 ダイアログ(Panel)自身と、その中に表示する文字、ボタンも、全てプレハブで作ります。 これは「prefab in prefab」という状態になり、後で説明しますが、ちょっとしたコツが必要です。 部品を全てプレハブとすることで、画像付きのダイアログなども簡単に拡張可能になります。 部品をプレハブにする関係で、下記の説明では部品に名前を設定しています。 サンプル通りの名前にしなかった場合は、スクリプトの該当箇所を修正してください。 プレハブと設定 1. ダイアログ Panel ダイアログそのものは、UI - Panel で作ります。 このパネルの中にテキストなどの部品を加えていき、部品の大きさによってダイアログの大きさも変化するように設定します。 UI → Panel を作成。名前は「DialogPanel」 表示したい Width を設定。この Width が最終的な見た目に影響します。 デフォルト設定では Image の Color のアルファ(「A」の値)が 100 ですが、このパネルを背景色とするため、A を最大値の 255 にしておきます。 Add Component で、Layout → Contentet Size Fitter 「Contentet Size Fitter」の Vertical Fit を「Preferrer Size」に Add Component で、 Layout → Vertical Layout Group 「Vertical Layout Group」の 「Control Child Size」→「Height」にチェックを入れます。 この設定で、部品の Height に応じて「DialogPanel」の Height も変わるようになります。 「Vertical Layout Group」の Padding に適当な値を入れておきます。デフォルトの 0 のままだと、メッセージがパネルの外側にひっついてしまいます。Spacing も設定し、オブジェクト間に隙間を持たせます。 この DialogPanel の Width から「Padding - Left」と「Padding - Right」を引いた値を、DialogPanel 内の部品 Width に使います。以下の説明を簡単にするため、この値を「コンテンツ Width」と表現することにします。 2. ダイアログ内の部品 この「DialogPanel」の中に部品を追加します。 (ヒエラルキーで「DialogPanel」を選択してから、右クリックの「UI」で部品を追加します) テキスト テキストもプレハブ化することにします。 プレハブ部品にしておくことで、後で画像付きダイアログを追加したいときとかの対応が柔軟になります。 「DialogPanel」の中に、UI → Text を作成。名前は「DailogText」とします。 「DailogText」の Width は、上で説明した「コンテンツ Width」にする。 適当にフォントサイズや行間などを設定してください(何かダミーのテキストをセットすると、調整しやすいです)。 OK ボタン まず Panel を作り、その中に Button を配置します。Panel が無いと、Button が左端に寄ってしまいます。 「DialogPanel」の中に、UI → Panel を作成。名前は「OkPanel」。 「OkPanel」の Width を「コンテンツ Width」に。 Image の Source Image は「None」に、Color の A は 0 に。 「Raycast Target」のチェックボックスを OFF にしておく。 ついでに「Maskable」も OFF にして良いと思います。 「OkPanel」の中に、UI → Button を作成。名前は「OkButton」。 Button の中の Text の文字列を「OK」に。フォントサイズを調整。フォントサイズに応じて Button の Width・Height も調整。 ここで、「OkPanel」の「Raycast Target」を OFF にするのが、超重要です。 普通はヒエラルキーの下にあるものが上に表示され、当たり判定も下にあるものが先になりますが、「prefab in prefab」で加えた部品は、親の当たり判定が子より優先されるようなのです。子は親よりヒエラルキーの下に表示されるので、一般的なヒエラルキーの感覚とは逆になります。 このため、OkPanelの「Raycast Target」が ON(デフォルト) のままだと、Panel の中にある Button が反応しなくなります。 この挙動を説明しているドキュメントが無く、原因を追及するのにかなり苦労しました。 OK・Cancel ボタン OKボタンと同じように、OK・Cancel ボタンを作ります。 「OkPanel」を Duplicate して作るのが楽です。 ヒエラルキー上の「OkPanel」を右クリックして Duplicate し、名前を「OkCancelPanel」に変更します。 「OkCancelPanel」の中の「OkButton」も Duplicate し、名前を「CancelButton」に変更します。 「OkButton」と「CancelButton」の Pos X を変更して2つのボタンを横に並べます。 「CancelButton」の中の Text の文字列を「Cancel」に プレハブ化 以上ができたら、全体的なサイズや Padding 値などを調整します。 こんな感じになりましたでしょうか? 調整が終わったらプレハブ化をおこないます。 プレハブ化の手順は以下の通りです。 ヒエラルキーを Load するためには Resources フォルダが必要なので、無ければ作ります。 Resources の下に「Dialog」という専用フォルダを作ります。 ダイアログで使用する全てのプレハブとスクリプトを1つのフォルダーにまとめておくと、Asset → Export で unitypackage ファイルにすることが出来、他のプロジェクトに Import しやすくなるのでお勧めです。 ヒエラルキーから「DailogText」「DialogOk」「DialogOkCancel」の3つをそれぞれ Projectの「Dialog」にドラッグしてプレハブ化します。プレハブ化したら(表示が青色になったら)ヒエラルキーで右クリックして Delete します。 「DialogPanel」をプレハブ化し、ヒエラルキーから Delete。 ここでは4つのプレハブを作るのが目的で、ダイアログがヒエラルキーに依存しなくて良いようにするために、ヒエラルキーからは削除しておくのです。 作られたプレハブは、Contentet Size Fitter の中にあったため、Height が 0 になっています。 そのままでは変更がしにくいので、Height に適当な値を設定した方が良いでしょう。 スクリプトから実際に Instantiate すると、Height は元通り無視されます。 と、ここまで書いておいてナニですが、プレハブを作るのが面倒くさいと思う方のために、unitypackage をココからダウンロードできるようにしておきました。 これを使って自分流にカスタマイズするのが楽だと思います。 スクリプト 「Dialog」フォルダ内に C# Scriptを作成し、名前を「MyDialog」とします。 この「MyDialog」をエディタで編集して下記のものに書き換えます。 ダイアログを呼び出す度にプレハブを Load するのは気が引ける(?)ので、全体をシングルトンにしてみました。 また、上述のように MonoBehaviour は継承していないので、プレハブを Instantiate する時にちょっと面倒な指定が必要です。メソッドを参考にしてください。 using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; /** <summary> 気軽に呼び出せるダイアログ </summary> */ public class MyDialog { private static MyDialog m_Instance = null; Transform CanvasTr; // ヒエラルキー上にある Canvas の Transform GameObject DialogPanel; // ダイアログ外観 Dictionary<string, GameObject> LoadedPF; // Loadした全プレハブ部品 Action ActionOK = null; // OKボタンが押された時の処理 Action ActionCancel = null; // Cancelボタンが押された時の処理 /// <summary> /// シングルトンの為のインスタンス /// </summary> private static MyDialog Instance { get { if (m_Instance == null) { m_Instance = new MyDialog(); } return m_Instance; } } /// <summary> /// コンストラクタ /// </summary> private MyDialog() { CanvasTr = GameObject.Find("Canvas").transform; // Canvas だけは、ヒエラルキーから拾う LoadedPF = new Dictionary<string, GameObject>(); // プレハブ保持用の連想配列 } /// <summary> /// メッセージをダイアログで表示して「OK」ボタンで待つ /// </summary> /// <param name="mess">表示するメッセージ</param> /// <param name="funcOk">「OK」ボタンが押された時の処理</param> static public void Alert(string mess, Action funcOk = null) { MyDialog dialog = Instance; dialog.DialogPanel = dialog.Instantiate("DialogPanel"); // ダイアログ外観 dialog.AddObj("DialogText").GetComponent<Text>().text = mess; // メッセージ dialog.ActionOK = funcOk; dialog.AddObj("OkPanel").transform.Find("OkButton").GetComponent<Button>().onClick.AddListener(dialog.ClickOkButton); } /// <summary> /// OK / Cancel 確認ダイアログを表示する /// </summary> /// <param name="mess">表示するメッセージ</param> /// <param name="funcOk">「OK」ボタンが押された時の処理</param> /// <param name="funcCancel">「Cancel」ボタンが押された時の処理</param> static public void Confirm(string mess, Action funcOk = null, Action funcCancel = null) { MyDialog dialog = Instance; dialog.DialogPanel = dialog.Instantiate("DialogPanel"); dialog.AddObj("DialogText").GetComponent<Text>().text = mess; dialog.ActionOK = funcOk; dialog.ActionCancel = funcCancel; GameObject buttonPanel = dialog.AddObj("OkCancelPanel"); buttonPanel.transform.Find("OkButton").GetComponent<Button>().onClick.AddListener(dialog.ClickOkButton); buttonPanel.transform.Find("CancelButton").GetComponent<Button>().onClick.AddListener(dialog.ClickCancelButton); } /// <summary> /// OKボタンが押された時の処理 /// </summary> private void ClickOkButton() { Close(); // ダイアログを消す if (ActionOK != null) { // コールバック先が登録されていれば ActionOK(); // 実行 } } /// <summary> /// Cancelボタンが押された時の処理 /// </summary> private void ClickCancelButton() { Close(); // ダイアログを消す if (ActionCancel != null) { ActionCancel(); } } /// <summary> /// 非MonoBehaviour 用の、プレハブ Instantiate(実体化) /// </summary> /// <param name="prefabName">Instantiate したいプレハブ名</param> /// <returns>生成された GameObject</param> private GameObject Instantiate(string prefabName) { if (!LoadedPF.ContainsKey(prefabName)) { // まだプレハブが Load されていなければ LoadedPF.Add(prefabName, (GameObject)Resources.Load("Dialog/" + prefabName)); // Load } GameObject obj = UnityEngine.Object.Instantiate(LoadedPF[prefabName]); obj.transform.SetParent(CanvasTr, false); obj.transform.localScale = Vector3.one; obj.GetComponent<RectTransform>().anchoredPosition3D = Vector3.zero; // anchoredPosition3D をセットしないと、Pos.Z が不定になる。 return obj; } /// <summary> /// DialogPanel に GameObject 追加 /// </summary> /// <param name="add">追加する部品名</param> /// <returns>追加された GameObject</param> private GameObject AddObj(string add) { GameObject obj = Instantiate(add); obj.transform.SetParent(DialogPanel.transform, false); return obj; } /// <summary> /// ダイアログを閉じる /// </summary> private void Close() { foreach (Transform child in DialogPanel.transform) { // 追加した GameObject を削除 UnityEngine.Object.Destroy(child.gameObject); } UnityEngine.Object.Destroy(DialogPanel); // ダイアログパネルを消す } } まとめ 「気軽に呼び出せる」ことと拡張が容易なことにこだわったのでスクリプトは長くなりましたが、使い勝手はかなり良いと思います。自分が過去に作ったプロジェクトも、このダイアログを使ってリファクタリングしたくなりました。 「Raycast Target」は、このダイアログの例では、DialogPanel とボタン以外は全て OFF にするのが良いと思います。デフォルトでは ON になっていますが、微妙に実行時間に負荷をかけていると思われます。 ここに掲載したサンプルは Panel の背景とかをデフォルト設定のまま使っているので、自分でカスタマイズして使ってください。 変更・再配布可・商用利用可。これ全体をアセットストアとかで有料販売するのだけはナシねぐらいの感じで使ってください。 最後に宣伝ですが、この記事が少しでも役に立ったという方は、人魚Daysアプリをダウンロードだけでもして頂けると励みになります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】シーン遷移すると OVRCameraRig が Missing になるんだけど?

この事件は 2021年9月7日に発生し、2021年9月8日に解決しました。現在も未解決です。 状況説明 MissingReferenceException: The object of type 'OVRCameraRig' has been destroyed but you are still trying to access it. 以下の状況で、コンソールに上記エラーが大量に発生する。送出元は不明。 プレイモード中にスクリプトからシーン遷移を行った場合 特に何もせず、起動直後にエディタ上で別のシーンファイル(同じ設定の XR Rig を含む)を選択・展開し、再生した場合 なお、起動直後の状態では再生してもエラーが出ないほか、エラーが出ていても、OVRCameraRig および XR Controller 等、関連オブジェクト・コンポーネントは不足なくシーン上に存在している。また関連性は不明だが、事象発生後、Ray Interactor のレイが実機上で不可視になる。 筆者は XR Interaction Toolkit をオーバーライドして使用していたため、当初はそれが原因かと思われた。しかし、Oculus Integration 付属の OVRCameraRig.prefab を空シーンに置いた場合にも同様の操作で問題が発生することが事件発生の翌日に確認され、別の問題が主要因であると断定された。 また Oculus Integaration も直近3つのバージョンを確認したが、いずれも同様の問題が起きた。 検証時に確認したパッケージ等 Unity 2019.4.22f1, 2020.3.0f1, 2020.3.16f1 Oculus Integration v29, v31, v32 XR Interaction Toolkit 1.0.0-pre.5 XR Legacy Input Helpers 2.1.7, 2.1.8 XR Plugin Management 4.0.7, 4.1.0 検証内容 複数のバージョンの Unity エディタで、設定項目やパッケージのバージョンの違いによる影響を確認。 解決方法 Verified ではないが、XR Plugin Management 4.1.0 を使用する。 不明。一時的な対処法として、XR Plugin Management をその時点で使用しているものとは異なるバージョンにすることが挙げられる。(再起動後、問題が再発することが判明。) 所感 いままで何故この問題が起きなかったのか、本当の問題はどこにあったのかは、未だ謎に包まれている。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む