その他
    ホーム 技術発信 DoRuby UnityでゲームのUI作りたい! 〜 物並べ編①
    UnityでゲームのUI作りたい! 〜 物並べ編①
     

    UnityでゲームのUI作りたい! 〜 物並べ編①

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    17新卒エンジニアがUnity関係で学んだことの備忘録を書きます。

    はじめに

    お初にお目にかかります、17新卒エンジニアの HN:inoooooocchi と申します。
    私は4月からエンジニアとして入社し、とあるゲームプロジェクトに配属され、サーバー側(Ruby及びRuby on Rails)とクライアント側(Unity(使用言語はC#))の両方の実装を担当させて頂いております。

    ところが私は、RubyもC#もUnityも、入社するまで(正確には3月にインターンシップで配属されるまで)一切触ったことがありませんでした。
    そんな中、ここ2、3ヶ月で様々なことを学び、そろそろ整理しておきたいな、と思った矢先にDorubyに記事を書くことになりました。
    従って私の記事では、私が仕事をしていて最も楽しい瞬間である「UnityでのUI制作」について、今まで学んだことの備忘録としての意味合いを兼ねてご紹介させて頂きます。

    ご注意

    本記事では、前章で述べた通りのUnity初心者中の初心者が試行錯誤して覚えた内容を載せています。
    従って、凄腕Unityユーザーの方々の中には、「もっと効率的な方法があるのに…」「本当にユーザーの事考えてる?」「え?入社してから今まで何やってたの?w」と思われる方がいらっしゃるかもしれません。
    優しい目で見守って頂けると幸いです。

    前書き

    • Unityって?
      それでは、早速本題に入っていきましょう。…と言いたいところですが、「そもそもUnityってよく聞くけど、どういうもんなの?」という方がいらっしゃるかもしれません。(私も1年ぐらい前までそうでした。)
      なので、かるーく説明したいと思います。
      Unityとは、ユニティ・テクノロジーズという会社が開発した、ゲームエンジン及びゲーム開発用プラットフォームのことです。
      とっても簡単に言えば、ゲームを作るための基盤を提供してくれる便利なツールってことです。
      GUI(マウスなどを用いて操作可能な直感的インターフェース)によって、ゲームを構成するオブジェクトの配置やオブジェクトを動かすためのスクリプトの設定が可能で、なんならプログラミングが一切出来なくてもゲームが作れてしまうという話もあります。
      (もちろん、自作のスクリプトを用いることでさらにゲームの幅が広がります)
      PC(Windows / Mac / Linux)、スマートフォン(iOS / Android)、さらにはPS4などのゲーム機、(Unityのプラグインを搭載した)Webブラウザなど、様々なプラットフォームに対応しているのも大きな魅力です。
    • UIについて
      ゲームを遊ぶためには、ユーザー(プレイヤー)が操作をする必要があります。
      もちろん、ユーザーがゲームを遊びやすくするために、開発側はなるべく見やすく・分かりやすく・操作しやすいようなUIを用意してあげなくてはなりません。
      例えば、1つのゲーム画面に全部で50個のボタンを配置する必要がある場合、「うまく入りきらないからめちゃくちゃ重なってるけど、頑張ってタップしてくれよな!」なんてUIは、「本当に遊んでもらう気があるのか?」と疑いたくなります。
      そこで、今回の記事では、そのようなふざけた画面を作らないために、「たくさんのオブジェクトを並べる」UIの作り方を書いていきます。

    1. SceneとCanvasの作成

    まず最初に、Unityでオブジェクトを配置するためには、SceneとCanvasが必要になります。

    用語解説

    Scene: 「.unity」という拡張子のファイルで、これが1つの画面に相当します。
         たくさんのScene(画面)遷移を繰り返し、ゲームが進行していきます。
         (例:タイトル→メニュー→ステージ選択→ステージ→リザルト→ステージ選択…)
    
    Canvas: Scene上にUIオブジェクトを配置するための、地盤となるオブジェクト。
    

    まずはSceneを作ります。Unityを開いたら、File -> New Sceneで、Untitledという無名のSceneが作られます。
    New Sceneでシーン作成
    作ったばかりの新しいSceneには、Main Cameraというカメラオブジェクトのみ存在します。

    次に、Canvasを作ります。Hierarchyの上で右クリックし、UI -> Canvasを選択します。
    Canvasを作成

    すると、Canvas(とEventSystem)が生成されます。EventSystemはUIの動作を管理する偉いオブジェクトで、これが無いとクリックや文字入力、タップ操作などが反応しなくなってしまいます。消さないようにしましょう。

    さて、このCanvasには、いろいろな仕組みが備わっています。Inspectorウィンドウで、選択したオブジェクトの情報を確認することができます。

    Inspectorウィンドウでオブジェクトの詳細情報を確認

    最初の設定として、カメラから見える位置にCanvas及びCanvas上のオブジェクトが来るようにします。

    Canvas という機能のRender Modeという設定を選択し、デフォルトの「Screen Space – Overlay」から「Screen Space -Camera」にします。

    Render Modeを変更
    すると、カメラを指定する部分(Render Camera)が現れます。

    Render CameraがNoneのままでは意味がないので、Sceneにもともと存在していたMainCameraをアタッチします。
    アタッチとは、スクリプトやオブジェクトを別のスクリプトやオブジェクトの構成要素に追加し、アタッチされた側から干渉できるようにするための行為です。
    今回の場合、MainCameraをCanvasにアタッチすることで、CanvasはMainCameraを認識し、使うことが出来ます。
    ドラッグ&ドロップで、HierarchyウィンドウにあるMainCameraを、InspectorウィンドウにあるRender CameraのNoneの部分に持って行きます。

    すると、無事カメラの設定が出来ました。
    これで、Sceneの作成とCanvasの作成は完了です。ようやくUIを作っていけます。

    2. オブジェクトを並べる領域を作る

    それでは、オブジェクトを並べる領域をUnity上で作成します。

    早速、先ほど作成したSceneのCanvasに、「オブジェクトを並べる領域」のオブジェクトを作ります。
    Canvasの上で右クリックし、Create Emptyを選択します。

    Create Emptyで空のオブジェクトを作成
    すると、Canvasの下に、GameObject(デフォルト名)が子オブジェクトとして生成されます。
    今回は、オブジェクトを並べる領域ということで、ObjectAreaなどの名前にしようと思いましたが、後々スクロール機能を追加したいので、InspectorウィンドウでScrollAreaという名前に変更します。
    ScrollAreaがCanvasの子オブジェクトとして存在
    生成されたGameObjectはデフォルトでは100×100pixelの小さな正方形なので、好きな大きさに変更します。サイズや位置の調整もInspectorウィンドウで行うことが出来ます。(マウスで掴んでびよーんと伸ばしたりも出来ますが、きっちりした値にするならInspectorがいいでしょう)
    ScrollAreaのサイズを変更
    今回は300×300の正方形にし、位置を左にずらしてみました。(x座標:0 → -160)
    ここで、Inspectorの座標値の左側に、四角形の記号のようなものが見えると思います。
    これは、Anchor(アンカー)といい、親オブジェクト(Canvas)に対して子オブジェクト(ScrollArea)の基準点がどこなのかを表しています。
    現在は中央に設定されているため、ScrollAreaに取っての基準点(0 , 0)が、Canvasの中央座標になっていました。それを左にずらしたので、x座標が-160という値になったわけです。
    本来ならプラットフォームごとの画面サイズの差を考え、このAnchorを調整しx,y座標が基準点から大きくずれないようにするべきですが、今回は省きます。
    このAnchorはUIのレイアウトを決める上で非常に大事なものなので、詳細は別の記事を設けて書きたいと思います。

    さて、これから、この300×300の領域にオブジェクトを並べていきます。

    3. ScrollAreaの中身を作る

    並べる領域が出来たので、今度はその中身を作ります。
    今回は例として、何らかのステージを選択する画面を想定し、各ステージの名前が書いてあるボタンを作ります。
    ボタンを作成するには、ScrollAreaの上で右クリックし、UI -> Buttonを選びます。
    Button作成

    SceneウィンドウでButtonが確認出来た

    はい、ボタンが出来ました。Buttonって書いてありますし誰がどう見てもボタンですね。
    本来なら、このボタンをクリックした時に別のSceneに遷移するなどのスクリプトを書いてアタッチするのですが、今回は省略します。
    ちなみに生成直後のButtonには「Image」「Button」の二つの機能が最初から搭載されています。画像を変えたい時はImageのSourceImageに画像をアタッチすれば変更できます。(今回は省きます)

    名前が「Button」じゃ寂しいので、適当に「StageButton」にしておきます。
    同様に、ボタンに書かれている文章も「Button」じゃ何のボタンかさっぱりわからないので、適当にステージ名を入れてみます。

    HierarchyのStageButtonの横に、▶︎があるのが確認できますよね。ここをクリックすると、Buttonの子オブジェクトを確認できます。

    ▶︎ をクリックして子オブジェクトを確認
    UI -> Buttonオブジェクトは生成されると同時にTextという子オブジェクトを持っています。このTextにInspectorウィンドウで文章を入れてあげれば、お好みの文字列をボタンに表示できます。
    また、領域の横幅が300pixelなので、違和感がないようにボタンの横幅も280pixel程度にしておきます。
    ボタンのサイズを調整

    これで、押してもうんともすんとも言わないボタンが出来ました。
    今回の趣旨はボタンを押すことでは無く、ボタンを並べ(て、スクロールさせ)ることなので、並べるためにボタンを大量生産しましょう。

    StageButtonの上で右クリックし、Duplicate(複製する)を選ぶと、ボタンが複製されます。
    もちろん、CopyしてPasteしても問題ありません。何度もやるのが面倒な人はCtrl + C -> Ctrl + Vしまくるといいでしょう。

    StageButtonを複製
    10個まで増やしてみた

    というわけで、StageButtonを9個増やして合計10個にしてみました。

    10個あるはずなのに1個しか表示されない

    …が、何故か表示されているボタンは1個だけです。何故でしょう?
    答えは簡単、「元のStageButtonの座標まで丸ごとコピーしてるから」ですね。つまり、1個にしか見えないStageButtonには10個のStageButtonが重なってしまっています。

    「え、じゃあオブジェクトを複製するたびに手入力で座標をずらしていかなきゃいけないの!?」

    もちろん、Unityには便利な機能があるのです。先輩に教えて頂いて、Unityの力ってスゲー!ってなりました。次の章でご紹介します。

    4. Vertical Layout Groupを使ってみよう

    その便利な機能とは、「Vertical Layout Group」という機能です。
    どんな機能か説明する前に、まずは試しに使ってみましょう。
    …と、その前に、機能の効果を体感しやすくするために一度複製したボタンを全て削除してください。
    複数選択して右クリックでDeleteすれば一発です。

    さて、StageButtonの親オブジェクトであるScrollAreaオブジェクトのInspectorをご覧ください。
    見事に何もないですね。そりゃそうです、Create Emptyで空のオブジェクトを作りましたから。
    オブジェクトに新たな機能を追加する場合、Add Componentをクリックします。Add Componentでは、Unityに標準で備わっている機能や、自作したスクリプトなど、様々な構成要素(Component)を追加できます。

    Add Componentで機能を追加
    早速、Vertical Layout Groupを追加してみましょう。
    Vertical Layout Groupを追加

    何故か大きくなりました。

    これはVertical Layout Groupの仕様であり、本記事の目的として不適切な動作なので、元に戻しましょう。
    InspectorのVertical Layout Groupで、Child Force ExpandのWidth、Heightのチェックを外しましょう。

    Child Force Expandのチェックを外した結果

    何故か小さくなりました。(左上)

    焦らないでください、これもそういう仕様なのです。
    Vertical Layout Groupは、子オブジェクトのサイズを自動で調整してしまうのです。
    もともとのサイズを維持したい場合、子オブジェクトに手を加える必要があります。

    Layout Elementを選択
    子オブジェクトのStageButtonのInspectorでAddComponentをクリックし、今度は「Layout Element」を選択します。
    すると、このようなものが追加されます。
    Layout Element設定
    詳細はここでは省きますが、ひとまず「Preferred Width」「Preferred Height」にチェックを入れ、現れたテキストボックスに、維持したい縦幅・横幅を入力します。
    280 x 30を記入

    すると…

    サイズが元通り

    あれほど縮こまっていたStageButtonが、Layout Elementの力により、本来のサイズ(=先ほど設定したサイズ)を取り戻しました。
    Layout Elementは、Vertical Layout Groupや、後述するHorizontal Layout Groupなど、
    「子オブジェクトのサイズを自動で調整してしまう機能」に対し、「このサイズにしろ!」と強制できる機能なのです。

    ちなみに、StageButtonが左上に寄ってしまっているのは、ScrollAreaのVertical Layout GroupのChild AlignmentをUpper Centerにすれば中央に戻せます。

    Upper LeftからUpper Centerに

    さらに、ScrollAreaに、「Content Size Fitter」という機能を追加します。

    その後、Vertical Fitの部分をPreferred Sizeに設定してください。

    Content Size Fitter導入

    Content Size Fitterは、そのオブジェクトのサイズを、全ての子オブジェクトのサイズに合わせて自動で変化させてくれる機能です。これが無いと、子オブジェクトを複製しすぎた場合、ぎゅうぎゅう詰めになって子オブジェクトが潰れて表示されてしまいます。

    さて、これで準備は整いました。Vertical Layout Groupは、ただ単に「子オブジェクトのサイズを勝手に変えてめちゃくちゃにする機能」ではなく、ここから彼(?)の本領が発揮されます。
    先ほどと同じように、StageButtonを10個ほど複製してみてください。

    StageButtonが並ぶ

    Vertical Layout Groupは、子オブジェクトを縦方向に自動で整列させる機能です。
    同様に、Horizontal Layout Groupは、子オブジェクトを横方向に自動で整列させてくれます。
    このようにして、無事に(?)ボタンを縦にたくさん並べることが出来ました!

    ただし、この状態で複製し続けると…
    はみ出る

    はみ出ます。(※Unity上のGameウィンドウです)
    ここで、スクロール機能が必要になってきますね。
    しかし、今回の記事がかなり長くなってしまうため、それはまたの機会にご説明いたします。

    5. 今後の課題&まとめ

    今回は、オブジェクトを並べる際の基礎の基礎をまとめてみました。
    今回作った機能を実際のゲームで運用させるためには、まだまだ課題があります。

    • スクロールして、はみ出た部分をクリックできるようにする
    • 「Stage1」の文字列を、Stage2、3、4…のように自動で変えたい(Prefab、スクリプトの使用)
    • StageButtonをクリックした時に、何らかの処理を発生させる(スクリプトの使用)

    次回からは、これらの課題を解決していきたいと思います。

    このように、私の記事ではUnityによるUI作成をまとめて行きたいと考えていますので、
    今回の記事を読んでUnityに興味が沸いた方、分からないことが解決した方(いるかな?)などなど、今後ともよろしくお願いします。

    以上、17新卒エンジニアのinoooooocchiでした。

    記事を共有