UIを作ろう のバックアップ(No.11)
- バックアップ一覧
- 差分 を表示
- 現在との差分 を表示
- 現在との差分 - Visual を表示
- ソース を表示
- UIを作ろう へ行く。
- 1 (2023-01-10 (火) 13:37:27)
- 2 (2023-01-16 (月) 17:44:08)
- 3 (2023-01-16 (月) 19:00:14)
- 4 (2023-01-17 (火) 13:04:56)
- 5 (2023-01-17 (火) 14:37:02)
- 6 (2023-01-17 (火) 15:50:43)
- 7 (2023-05-01 (月) 11:28:39)
- 8 (2023-05-02 (火) 00:16:21)
- 9 (2023-05-02 (火) 01:51:35)
- 10 (2023-05-02 (火) 02:54:09)
- 11 (2023-05-02 (火) 04:21:11)
- 12 (2023-05-02 (火) 22:27:53)
- 13 (2023-11-15 (水) 19:26:15)
contents
はじめに
アイテムを設置しようの続きになります。
今回のテーマはUI(User Interface)の追加です。
br
UI とは、プレイヤー( = User )とゲームとをつなぐ箇所( = Interface )という意味で、
ここでは、その仕組みのことを指します。
マップやガイド、スコア表示、体力ゲージ、ボタン、メッセージなどがこれに当たります。
br
このゲームでは、
・ゲームシーンでの現在のタイムスコア(経過時間)のテキスト ・スコアシーンでのリスタートボタン ・スコアシーンでの最終的なタイムスコアのテキスト
を作っていきます。
一つずつ実装していきましょう。
br
タイムスコア(ゲームシーン)の設置
まずは、ゲームシーンでのタイムスコアのテキストを表示してみましょう。
br
UIの設置は、ほかのゲームオブジェクトと同様に Hierarchy を右クリックしてできます。
UI>Textを選択して名前を「Timer」とします。
これで、Hierarchy に Canvas と EventSytem が追加されて、
Canvas の中に「Timer」という名前でテキストUIができます。
画面のどこかに(おそらく画面中央)に「NewText」と文字が出てきてるかと思います。
br
Canvas とは、UI を描画するための(抽象的な)領域です。
一般的なオブジェクトと、UI オブジェクトは別々の領域で描画されます。
ですので、UI オブジェクトは Canvas の子要素として、作成・管理します。
br
EventSystem とは、ユーザーからの入力を受け取り、 UI に対してイベントを送信するためのものです。
例えば、「ボタンに対するマウスクリックを、ボタンに伝える」といった感じです。
br
次は、テキストUIの大きさや位置を調整します。
現在、このゲームのカメラは解像度が未固定で、ゲームのウィンドウの大きさに自動で調整されるようになっています。
しかし、キャンバスの初期状態では、UIはpixel固定であるためゲーム画面の大きさで表記に乱れが出てしまいます。
そこで、 Canvas の Inspector から CanvasScaler > UIScaleMode を ScaleWithScreensize に設定を変更してあげることで、
カメラと同じようにウィンドウサイズに合わせてUIの大きさが変わるようにできます。
そして、「Timer」の Inspector の RestTranform から( posX 、posY 、Width 、height )、Textから( FontSize 、Paragraph > Aligment )を変更します。
それぞれ「キャンパスに対する相対位置(x座標、y座標)」「UIの幅、高さ」「文字の大きさ、UI内の位置」に当たります。
見やすい位置、大きさになるように調整してみましょう。
次に、経過時間を出力する機能をスクリプトで書いていきます。
「Text」の Inspector から AddCompornent > NewScript を押して「Timer」という名前でスクリプトを追加します。
br
geshi
br
名前空間として、UnityEngine.UI を追加しています。
UnityEngine.UI はUI関連を扱う名前空間で、UI関連の処理を行うときに使われます。
Update() 関数の中で出てきた Time.time というのは、ゲーム(シーン)開始からの時間を扱う変数です。
これを定義・取得した Text コンポーネントの text 変数に代入することで、現在の経過時間を出力できます。(Text コンポーネントは UnityEngine.UIの名前空間を用いることで使えるようになります)
ただ、「totalTime」は float 型なのに対して、Text.text は string 型しか指定できません。
そこで、toString() メゾットという、数値型を string 型に変更するメゾットを用いて、totalTime を string 型に変えます。
br
次に、出力のフォーマットを「○○:○○(分:秒)」形になるように改良してみます。
br
geshi
br
まず経過時間をまとめて「totalTime」で取得していたものを分と秒でそれぞれ分けて取得します。
分は総経過時間を60で割ったもの、秒は60で割った余りになるので、それを代入しましょう。
「/」が「割る」演算子、「%」が「余り」の演算子になります。
最後に、書式設定をします。
toString() メゾットは、引数に表記のフォーマットを指定することで簡単に書式設定が可能です。
「D2(D=10進数表記、2=2桁、足りない部分は0埋め)」に設定すれば完成です。
br
リスタートボタンの設置
次は、リスタートボタンの設置をします。
まず「Score」シーンに移動します。( Assets > Scene > Score を押すとシーンが変わります)
真っ青な画面になって、 Hierarchy を見てみるとカメラしかない状態になったと思います。
オブジェクトはシーンごとに管理されるので、別のシーンに移るとこれまで作成したオブジェクトはなくなります。
カメラはどのシーンにも作成時にデフォルトでできるのでカメラだけのシーンになったわけです。
そのため、これまで指定してきたカメラ、アスペクト比の設定等がこのシーンでは設定されていません。
ということで、最初にシーンの各設定を行います。
それぞれ以下のようにします。
シーンウィンドウの解像度設定を「16:9Aspect」にする カメラの Inspector から Camera > Projection > orthographic にする(このシーンはUIしか置かないのでしなくてもよい)
br
そうしたら、ボタンUIを追加します。
テキストUIと同様に Hierarchy を右クリックして、UI > Button を押して名前を「Restart」とします。
忘れずに「Canvas」の Inspector から CanvasScaler > UIScaleMode を ScaleWithScreenSize に設定しましょう。
次に、ボタンとその中の文字の位置、大きさを調節します。
ボタンの位置、大きさは、ボタンの Inspector 、RestComponet の position、 width、 height から、
文字の位置、大きさは、ボタンの子要素にある Text オブジェクトの Inspector 、 Text の FontSize 、 Paragraph から調節します。
文字の内容は、同じく Text オブジェクトの Inspector 、 Text の Text 要素を書き換えれば指定できます。
br
最後に、ボタンが押された時の動作を作ります。
まず、「任意のタイミングでシーンの移動をしてくれる」オブジェクトを作ります。
Hierarchy を右クリックして CreateEmpty から「MoveScene」という空のオブジェクトを作り、
このオブジェクトに Inspector から AddComponent > NewScript から「MoveScene.cs」という名前でスクリプトを作ってください。
br
geshi
br
このオブジェクトは任意のタイミングでシーンの移動を行いたいため、Start() 関数やUpdate() 関数は使いません。
代わりに新しく「OnClick」という名前の関数を定義します。これで「OnClick() 関数を呼び出したとき」シーンの移動が行われるオブジェクトが出来上がります。
br
では最後に、先ほど作ったボタンが押されたら、この「MoveScene」オブジェクトの OnClick() 関数を呼び出すようにしましょう。
ボタンの Inspector から Button > OnClick()(Noneとある所)に、「MoveScene」オブジェクトをドラッグ&ドロップして、
NoFunction > MoveScene > OnClick() を押します。
これで、ボタンを押したら「MoveScene」のOnClick関数が呼び出さる。つまり、シーンが移動する、という機能が出来上がりました。
br
タイムスコア(スコアシーン)の設置
最後に、スコアシーンにゴールした時の時間、タイムスコアを表示でいるようにします。
一見簡単そうですが、実は意外と複雑なことをする必要があります。
というのも、少し前にも言いましたがゲームオブジェクトはシーンごとに別々に管理されています。
時間を集計するスクリプトもゲームオブジェクトのコンポーネントとして動作しているため、
シーンを移動するときにその記録がオブジェクトと一緒に消されてしまいます。
そこで、シーンからシーンにデータを引き継ぐ際は、
1.引き継ぎたいデータをもつオブジェクトをシーンの移動の際に壊さないように設定する 2.引き継ぎたいデータをオブジェクトに依存しない形に保存して、移動後に読み取る
などがあります。(他にもいくつか方法はありますがこの2がスタンダード?)
1.は直感的にできて作りやすいですが、シーンの移動が多くなるとオブジェクトが溜まっていき処理が重くなっていってしまいます。
それに対して2.は難しさを感じますが、覚えれば非常に簡潔にかつ汎用的に使うことができます。
もちろん2.にもデメリットはありますが、基本的にこの方法を使っていくといいと思います。
ということで、今回も2.を使っていきます。
br
実装の流れは、
・ゴール直後、シーン移動直前にスコアの保存を行う ・シーン移動後スコアを受け取る ・スコアをテキストUIとして出力する
となります。
br
スコアを保存する
スコアの計測は「Timer」オブジェクト、ゴール判定は「Goal」オブジェクトと、それぞれ別オブジェクトで処理しているため、
まず、スコアを保存する前準備として、「Goal」オブジェクトから「Timer」オブジェクトのコンポーネントを取得して、
ゴール直後に「Timer」オブジェクトからスコアを、「Goal]オブジェクトに渡せるように編集します。
br
geshi
br
geshi
br
「Timer.cs」でタイムスコアを変数として毎フレーム保存し、「Goal.cs」で「Timer」コンポーネントを取得して、
「Goal」オブジェクトでタイムスコアを取得できるようになりました。
br
次は、そのスコアをゲームオブジェクトに依存しない値として保存するスクリプトを作っていきます。
これまではゲームオブジェクトにコンポーネントとしてスクリプトを書いてきましたが、「ゲームオブジェクトに依存しない」ということで、
独立したスクリプトを書いていきます。
アセットを右クリックして、Create > C#Script を押し、「Data」としてスクリプトを作ります。
br
geshi
br
今まで気にすることのなかった MonoBehaver ですが、
ここではクラスの継承といわれるものをしていました。
クラスの継承とは、あるクラスから性質を受け継いだ新しいクラスを作ることです。
Start() 関数の呼び出しをする必要がなかったり、スクリプトをオブジェクトにコンポーネントとして付与できていたのは、実はこの MonoBehaver クラスのおかげでした。
しかし、前述の通り今回のコードはゲームオブジェクトに依存しない独立したものになるので、クラスの継承が不要というわけです。
代わりにつけた static は、「静的」という意味の修飾子です。
今回はクラスについているので、静的クラスとなります。
静的クラスは、プロジェクトの実行時にクラスそのものが変数・関数などを持ちます。
アイテムを設置しようでやりましたが、一般的にクラスはインスタンスを生成して、
インスタンスごとに変数・関数などを生成・保持していますが、
静的クラスの場合はその必要がありません。(正確にはインスタンス化できません)
今回はアクセス修飾子が public なので、これでプロジェクトのどこからでもインスタンス化することなく直接クラスの操作ができます。
MonoBehaver を継承した一般のクラスの場合は、生成したインスタンス、つまりコンポーネントをシーン移動時に削除されてしまいますが、
こうすることで、シーン移動後も値を保持できるようになります。
br
クラスの中の「public static int score = 0;」にも、 static がついていますが、
静的クラスのメンバーはすべて静的メンバーである必要があります。( static を必ずつけます)
ちなみに、 static のついていない一般のクラスは static のついたメンバーを持つことができます。
その場合、そのメンバーは各インスタンスで共有されるメンバーとして定義されます。(変数の場合、共通の値にあります)
br
geshi
br
これまでは、他のスクリプトの操作をするとき、インスタンス(コンポーネント)としてスクリプトを取得する操作が必要でしたが、
今回は「Data」クラスのメンバーを直接変更できます。
これで、「Data」クラスの「Score」変数として、スコアの保存ができました。
br
保存したスコアを表示する
次に、「Score」シーンで、保存したスコアを取得し、表示します。