ゲーム構造基礎構築(Unity)
アウトゲーム簡易UI構築
目的
汎用的なアウトゲーム要素を作成しておき、色々なプロジェクトで使いまわしたい。Mobile,Web,Console全てに対応したものとしておく。
要件
- タイトル
- ゲームスタートボタン
- 押しても反応しない
- クレジットボタン
- 素材分類ごとの表記欄(2DArt ,3DArt ,Font,Music、Special thanks)
- 作者リンク、コピーライト
- 閉じるボタン
- オプションボタン
- 音声に関する項目
- BGMバー サンプル音声
- SEバー サンプル音声
- Voiceバー サンプル音声
- デフォルトに戻すボタン
- セーブデータ関する項目
- セーブデータをリセットするボタン
- ゲームの進行度表示
- ランキングに関する項目
- あなたの名前
- 入力欄の表示(Mobile>ソフトウェアキーボードorWeb>キーボード)
- 現在の順位
- あなたの名前
- キーコンフィグ
- 項目A、現在の設定
- 項目B、現在の設定
- 項目C、現在の設定
- 入力方向に関する項目、現在の設定
- デフォルトに戻す
- 描画処理に関する項目
- 閉じるボタン
- 音声に関する項目
- プライバシーポリシーボタン
- 押したら外部リンク(将来的にAndroid,iPhone両方で使うため)
- 背景画像
- タイトル画像
- アプリバージョン
- ゲームスタートボタン
分類別実装方法について
事前設定
Projectウィンドウにて以下のフォルダをAssetsフォルダ下に追加。Fonts,Images,Soundsには素材データを放り込む。
- Fonts
- Images
- Prefabs
- Scripts
- Sounds
趣味で2D Pixel Perfectを利用する(Menu>Window>PackageManegerから最新版を)合わせて、以下のことを設定する。参考はUnity公式の2D pixel perfectのページで
- 開発環境の設定
- 編集のしやすさを上げるため画像(Sprite)移動やセット時の移動幅をSnap Setting で変更する
- Edit>Snap Settingsを開き、MoveX,Y,Zを1/Asset Pixels Per Unitの数値(例:1/16=0.0625)にする
- あ
- あ
- 編集のしやすさを上げるため画像(Sprite)移動やセット時の移動幅をSnap Setting で変更する
- デフォのMainCameraのGameObjectにPixel Perfect Cameraのコンポネントを追加
- Assets Pixel Per Unitにタイルサイズなど基準とした値を指定する(Xpixel:1mのXを指定。16とか32とか。2の乗数が処理効率の面から見て画像と親和性が高い。)
- Reference Resolutionに実現したい基準解像度を入力(640x360,1024x768,NES=256x240、ひとまず320x180としておく)
- Upscale Render TextureはReference Resolutionで設定したものにできるだけ近い解像度でシーンがレンダリングされた後、実画面に合わせて拡大縮小されます。→kwsk
- Pixel Snapping をONで小数点以下の画像ずれをカメラが補正する様に(Upscale Render Textureが無効でのみ利用可)
- CropFrameは基準解像度と正確に一致するようにワールド空間の表示領域がトリミングされる。また、画面隅の隙間を埋めるように黒い余白が追加される→kwsk
- Stretch Fillは基準解像度を維持しつつ無理やり全画面で詰める設定(私は基準Pixelが長方形になっちゃうのが嫌でOFF)
- Projectに取り込む画像File(以下Sprite)全てにテクスチャーインポーター(Projectフォルダで画像ファイルを選択するとInspectorに表示されるソレ)で次の設定を施す。
- まずはTexture TypeをSprite(2D and UI)へ変更。次のプロパティが出現するので変更していく
- MipMapは生成しない
- PixelPerUnitを同じにする。Pixel Perfect CameraコンポネントのAssets Pixel Per Unitプロパティと一致させる。
- Mesh TypeをFull Rectにする
- Filte ModeをPoint(no filter)にする
- Compression(圧縮フォーマット)をNone(無圧縮)にする
- Max Sizeを適度に小さくする。(Max Sizeを超える画像であった場合はMax Sizeに収まる大きさまで縮小して取り込まれる。Pixel Artは縮小されると崩れるので縮小はさせない方向で)ただし、画像ファイルはなるべく1枚にまとめる(SproteAtlasにする)方が最終的な容量は減る=このため、Max Sizeが大きくなってしまう場合は・・・しゃあない(その分ファイル数が少なくなるし)
- Sprite Editorを開き、Spriteの小窓の設定において、Pivotプロパティを「Custom」に、Pivot Unit Modeを「Pixels」へ変更する
2D Pixel Perfectを使うなら知っておきたいこと
- ユーザーの持つ端末の解像度よりも絶対大きくならない様な基準解像度(Reference Resolution)を設定する(例:320x180とか)
- 90,180,270度以外の回転をさせたいorエフェクトかけたいならUpscale Render TextureをONに
- Upscale Render Textureを使用しないならCrop FrameのXかYはONにする
- Upscale Render Textureを使っても綺麗に見えないときはX軸やY軸をトリミングする→kwsk
- Standalone想定なら環境的に多くのユーザーが16:9端末を使っているので基準解像度もそれを意識する
Unityにおける画面の概念
ゲームの「場面」を区切る仕組み→Scene
ゲームの「画面」はScene内部をどのようにカメラで写すかで決まる。
さらにそれとは別に、
- 画面に直接貼り付ける→Canvas
- カメラの撮影結果全体に効果を与えるPostEffect
アウトゲームであれば大半はゲーム本編(=インゲーム)とは無関係の設定や選択肢の組み合わせで表現できることからCanvasを中心に考えて構築する。
Hierarchyで右クリック>UI>Canvasを追加します。(合わせて、EventSystemのGameObjectが勝手に追加されますが後述)。以下、この「CanvasのGameObject」をMainCanvasと略記します。
MainCanvasの以下のプロパティを設定する。(いずれもPixel Perfectを意識して設定)
- CanvasコンポーネントのRender Modeを「Screen Space Camera」へ変更
- CanvasコンポーネントのPixel Perfectにチェックを入れる
- CanvasコンポーネントのRender Cameraに「Main Camera」を登録
- CanvasScalerコンポーネントのUI Scale Modeを「Scale with Screen Size」へ変更
- CanvasScalerコンポーネントのReference Resolution を基準解像度と同じ値にする
- CanvasScalerコンポーネントのScreen Match Modeを「Shrink」に変更する→要調整cameraの影響受けるのか受けないのか?
- CanvasScalerコンポーネントのReferrence Pixels Per Unitを16へ
画像の配置
次の画像を配置していく。
- タイトル背景
- タイトル
- UI枠
- 画像ボタン
- 上記以外の画像
タイトル背景とタイトル
そこにあるだけで特別な効果を持たない画像。
MainCanvas選択後、右クリック>UI>ImageからImageのGameObjectをMainCanvasの子供として2個追加する。
Source Imageプロパティへ用意した素材を当てはめる。
ついでに配置のしやすさ的な部分を考慮して、Anchor Presetを「bottom left」(=左下基準)に変更
UI枠
さらにImageのGameObjectをMainCanvasに追加する。次に追加する画像はUI上に表示する各項目別の枠としたいので9sliceを採用した。
9slice(ナインスライス)はメッセージウィンドウやUIの枠を作る用の画像分割テクニックの一つ。
1枚の画像をテンキーでいう1~9にあたるパーツに分け、枠として利用しようというもの、パーツはそれぞれ
- 1,3,7,9は枠の4隅
- 2,4,6,8は上下左右で拡大縮小される枠
- 5は枠中心部の背景
のような役割として利用する。
通常はグラフィック編集ソフトウェアなどを利用するが、Unityでは画像が用意できればUnity Editor上で作成が可能。やり方は、9sliceしたい画像をProjectウィンドウで選択した後、Inspectorの表示項目から「Sprite Editor」のボタンをクリックし、表示されたSprote Editor Windowで編集する。
ひとまずは、Borderプロパティに存在する4つの項目LTRBの値を指定する。
なお、Sprite Editor Windowは拡張機能のため、インストールされていない場合はクリックしても以下の旨が表示されて利用できない。(→なければPackageManegerから2D Spriteのパッケージをインストールする)
インストールされていない場合に表示されるメッセージ
No Sprite Editor Window registered. Please download 2D Sprite package from Package Manager.
ここまでが準備、次にuGUIでの利用方法。
Imageのゲームオブジェクト(Canvas>Image)で9sliceした画像をSource Imageとして選択した後、Image Typeプロパティで「sliced」もしくは「Tiled」を選択する。Imageを拡大縮小した場合に、slicedは2,4,5,6,8に該当する部分を「引き伸ばす」方式で、Tiledは2,4,5,6,8に該当する部分が一定の大きさを超えるたびに同じ画像を「増やして並べる(Tileする)」方式。(4,6は上下、2,8は左右、5は上下左右)
画像ボタン
画像のうち、「何かしらの入力に反応する効果を持つ」画像のことを画像ボタンと呼ぶケースがある。
後述の「ボタンの設置、イベント処理」を参照。
それ以外の画像
実際は記載した以外にも画像が必要となる場面は多いが、前述のいずれかに該当するものと思われるので、必要になったところで追加していく。
テキスト表示、配置、フォント
HierarchyのMainCanvasで右クリック>UI>Textを追加する。
CharacterセクションのFontプロパティに外部から入手した任意のフォントを当てはめると良い。(デフォルトのものをそのまま使っても良い。)自分はPixelアートに統一をしたかったので、Pixelチックで無償のフォントを入手してProjectフォルダからドラッグ&ドロップで登録した。続けて、Textプロパティに任意のテキストを入力する
- テキスト描画
- テキスト配置
- フォントファイルの利用
ボタンの設置、イベント処理
HierarchyのMainCanvasで右クリック>UI>Buttonを追加する。
ButtonにはデフォルトでTextオブジェクトを子供として持っており、これがボタン上に表示されている「Button」の文字ということになる。
また、Buttonには画像を設定することができる。
Buttonのゲームオブジェクト下にImageコンポーネントがあり、そこにSource Imageプロパティが存在するのでProjectウィンドウからButtonの背景としたい画像をドラッグ&ドロップで登録すると良い。
Buttonにはフォーカスというものがあり、キーボード操作に対応させるとなるとこれを維持したほうが良い。また、無効にしたい場合はNavigationプロパティをNoneとする。
- ゲームスタートボタン
- クレジットボタン
- オプションボタン
- デフォルトに戻すボタン
イベント処理
C#スクリプトがここで登場。
まず事前に用意してあったScriptフォルダをProjectウィンドウから開く、右クリック>Create>C# Script。洗濯後にファイル名が光るのでキーボードを打つと名称を任意のものに変更できる。ここでは、仮に「Sample Button」と記入後Enterキーで決定。
ダブルクリックを行うとVisualStudioなどのエディタで中身が開けるので、次のように書き換える。
'''C#:SampleButton.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SampleButton : MonoBehaviour
{
public void OnClick(){
Debug.Log("Button Clicked!");
}
}
'''
上書き保存後、Unityに戻ってくると変更内容の反映が自動で行われる。
その後、ProjectウィンドウのSampleButtonをHierarchy上のButtonに該当する任意のGameObjectへドラッグ&ドロップし、アタッチ(コンポーネントの追加)を行う。
ここでは、仮にButton_ToInGameを対象にドラッグ&ドロップをしたものとする。
続けて、HierarchyにてButton_ToInGameをクリック、InspectorからButtonコンポーネントのOn Click()プロパティを探す。イベント処理を実装するためにこのプロパティでは次の3つのことを行う。
- ボタンクリックの実行タイミングの制御
- どのButtonオブジェクトがクリックされたことを検知したいのか(イベント発生源)
- イベント発生源でイベントが起きた時に処理したい関数は何か(ファンクション)
順番に次のように設定をしてください。
- デフォルトが「Runtime Only」なのでそのまま=実行時のみにクリックイベントを発生させる
- ◎をクリックすると「Select Object」の小窓が出現するので「Button_ToInGame」を探して決定する
- No Functionの枠をクリックすると、Objectのリストが表示される。この中から、SampleButton>OnClick()を選択。(実際関数名は何でも良いようで、public修飾子はついている必要有)
続いて、外部リンクへ行くための処理が書かれたスクリプトのサンプル。前述の流れと完全に一緒で、違いがあるとすればスクリプト内部の処理にブラウザを起動して外部リンクを参照するコードが入っている程度。
'''C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SampleExternalLinkButton : MonoBehaviour
{
public string url = "http://google.co.jp";
public void OpenExternalLink(){
var uri = new System.Uri(url);
Application.OpenURL(uri.AbsoluteUri);
}
}
'''
続けて、「他のCanvasを開くスクリプト」のサンプルを表示する。私はアウトゲームの画面切り替えにキャンバスを利用しており、クレジット画面、オプション画面を1キャンバスとしてそれぞれ作成したためこういうスクリプトが必要になった。やっていることは単純で各キャンパスのアクティブのONOFFを切り替えているだけ。また、流用ができるように汎用的な記述を採用している。利用前にprebCanvasプロパティとNextCanvasプロパティに移動元のキャンバスと、移動先のキャンバスのGameObjectをHierarchyウィンドウからドラッグ&ドロップしましょう。なお、このスクリプトに画面を跨いだデータ保持の処理は含めていません。
'''C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SampleCanvasChangeButton : MonoBehaviour
{
public GameObject prevCanvas;
public GameObject nextCanvas;
public void ChangeCanvas(){
if(prevCanvas==null || nextCanvas== null) return;
nextCanvas.SetActive(true);
prevCanvas.SetActive(false);
}
}
'''
この他にもスクリプトが必要なボタンは存在するが、他の画面部品が影響するため後述する。
スライダー
HierarchyのMainCanvasで右クリック>UI>Sliderを追加する。Sliderで次の要素を実装する。
- BGMバー サンプル音声
- SEバー サンプル音声
- Voiceバー サンプル音声
- ゲームの進行度表示
まず、Sliderを追加すると中身として次の構造物でできていることがわかる
- Background : スライダーの背景、移動可能な範囲の部分
- FillArea:スライダーの背景、Backgroundのうち、選択された(メモリに該当する)部分。可動域。
- Handle Slide Area : マウスのドラッグで操作するスライダーのツマミのこと
今回は構造物は基本変更せずにそのまま利用する。
Heirarchyウィンドウ上のSliderを選択した状態でInspectorに表示されている項目の中から、Valueプロパティを探す。これはスライダーのメモリをどのくらいの状態にしておくかのパラメータです。デフォ値が0になっているので、ひとまず0.5にする。
なお、Sliderはデフォでは0~1の範囲でfloat型の設定になっているが、Whole Numbersプロパティにチェックを入れることで範囲をInt型に変更できるようになる。今回はそのままで。
スクロールバー
Notスライダー。テキストなどのスクロール用の画面部品がスクロールバー。
Webなどでお馴染みの「決まったコンテンツを上下(左右)に動かすスクロールバー」を実現したい場合は、
単純なスクロールバーよりもスクロールビューの方がおすすめ。HierarchyのMainCanvasで右クリック>UI>Scroll Viewを追加する。
スクロールビューの外観を調整する
さて、スクロールビューはデフォルトでは上下と左右、双方のスクロールを有する。これを1方向のみに限定したい場合はScrollViewのScroll RectコンポーネントにHorizontalとVerticalのプロパティがあるのでこちらのチェックを外せば良い(ただし、編集中は見た目が残る。消したい場合は、Scroll Viewの構造物から該当するものを除去する。合わせてwidthやbottomなどを微調整しないと見た目は違和感が出る)。
- ViewPort :スクロールバーで上下もしくは左右で「動かしたいもの」を収めるためのオブジェクト
- Scrollbar Horizontal:水平(左右)方向のスクロールバーと関連するコンポーネントをまとめたオブジェクト
- Scrollbar Vertical:垂直(上下)方向のスクロールバーと関連するコンポーネントをまとめたオブジェクト
また、スクロールバーの太さなどはScrollbar HorizontalもしくはScrollbar VertivalのWidthやHeightを変更することで調整できる。
なお、Unity2020.3ではScroll ViewのInspectorにて、Spacingがデフォルトで-3に設定されており、水平と垂直スクロールバーがお互いちょっとめり込んでいたのでこれも0に変更して調整した。
実行後に実際に操作をするとスクロールの仕方に若干の違和感を感じた。これはScrollViewのInspectorにてScrollRectコンポーネントのMovement TypeプロパティをデフォルトのElasticからClampedに変更することで軽減された。
スクロールビューに中身を追加する
何をどうスクロールに含めるかはContentゲームオブジェクトにどんなコンポーネントを加えるかで選択肢が変わってくる。
- Contents Size Fitter
- Text
- Layout系コンポーネント
Contents Size Fitterコンポーネントはまずmust。中身に収めるものが多くなりすぎた場合でもスクロールバーの形状を維持(=フィットしたままに)できる。
続いて、Textコンポーネント。ScrollViewに含めたい内容がシンプルな1つの長文である場合はこのコンポーネントを加えて必要なテキストメッセージを記載するなり、スクリプトで処理するなりすれば良い。
最後にLayout系コンポーネント。選択肢としてはVertical Layoutであるとか汎用的なのはGrid Layoutか。ScrollView内部でスクロールする内容が様々なものになってくる場合は、Contentを親とした子として付け足していく方が個人的には性に合う。それを実現したい場合は、Layout系コンポーネントが選択肢となる。(Layout系コンポーネントをつけない場合は・・・表示されない?どこか遠い場所に設置されてるようだ?縦幅or横幅がデカすぎたとか?)
チェックボックス、ラジオボタン
デフォルトで用意されていません。ということで自作する!!(衝撃)
HierarchyのMainCanvasで右クリック>UI>Toggleを追加する。ToggleはONOFFを表現できるボタン。(チェックボックスのような)スイッチ+テキストで構成されており、スイッチの部分が四角い背景とその子供にチェックマークを保有している。
- ゲーム全体の難易度設定など
- 描画処理に関する項目内に存在するONOFF
これをサクッとラジオボタンのように運用するには、まずスイッチ部分の四角い背景とチェックマークをそれぞれ順番に丸い背景、丸い画像に変更する。Projectウィンドウ上で右クリック>Create>2D>Sprite>Circleでプリミティブな2Dの円画像を生成し、Hierarchy上のToggle配下にあるBackgroundとCheckmarkの持つImageコンポーネント>Source Imageプロパティを前述の生成した画像で置き換えると良い。(色とサイズについてはそれぞれでInspector上で微調整設定すれば良く、円画像は1つ生成するだけで問題ない)
これに加えて、ラジオボタンの実現には、「ラジオグループ」という概念が必要になる。
チェックボックスと異なりラジオボタンは「複数の中から一つを選ぶ」という性質を持つ。このため、この性質を何かしらの手段で実現せねばならない。スクリプトという方法もあるが、Toggle Groupコンポーネントというものが存在するらしいのでそれを利用する。
使い方。まず、ラジオボタンとしたいToggleのうちから一つを選び、「Add Component」ボタンなどからToggle Groupコンポーネントを追加する。続けて、同じグループとしたいToggle「全て」(Toggle GroupコンポーネントをつけたToggle自身も含む)を対象にToggleコンポーネントにあるGroupプロパティへ「Toggle Groupコンポーネントも持つGameObject」(=ここでは一番最初のToggle)をアタッチする。
シークバーとボタンを絡めたスクリプト
シークバーでVolume調整操作周りの実装を行う。
- シークバーで該当する分野のボリュームを変更する
- ♪ボタンを押すことでサンプルを流す
- デフォルトボタンを押して音量をデフォルトに戻す
続けて、BGM、SE、VoiceなどVolume調整は全て記録しておき、SaveとLoadを行うようにする。
特別暗号化する必要のあるデータでもないため、PlayerPrefsを使ってサクッと実装する。
'''C#
public void LoadSliders(){
foreach(Slider slider in sliders){
slider.value = PlayerPrefs.GetFloat(slider.name,slider.value);
}
}
public void SaveSliderValue(Slider slider){
float value = slider.value;
PlayerPrefs.SetFloat(slider.name,value);
PlayerPrefs.Save();
}
'''
Slider変化時に反応してSaveを行う。Slider変化のたびに保存処理するので実際はあまり良い例とは言えないが、まぁ今回はこれでよしとする。なお、SliderのOnValueChangedには引数を1つつけることができ、事前にスクリプトとしてこれが引数!というのを設定しておき、Inspector上で引数を与える欄が増加するので、型の合うこのデータ!っていう変数(もしくはゲームオブジェクト)を与える。Slider自身を引数に入れることによってスクリプトの処理内容を引数に入れたSliderによって変化させる汎用的なものを作れた。
配置の調整
ここまでくると画面部品がかなり増えてくるので、配置を調整する。
- 入れ子
- アンカー
UnityではHierarchyでの親子関係が移動に影響を及ぼす。具体的には、親オブジェクトが移動すると子オブジェクトも移動してしまう。逆にそれを考慮して、ひとまとまりのUI部品を作り、まとめて動かすこともあり。
ダイアログ
Unity上に概念として存在しないので自作することになる。
- 本当に○○してよろしいですね?(Y/N)
画面遷移、URL遷移(アプリ外部への投げかけ方)
音声再生
からのゲームオブジェクトを作って「AudioManager」と名付ける。
AudioSourceコンポーネントを追加して、AudioClipプロパティにProjectウィンドウから音声ファイルをドラッグ&ドロップで登録する。
テキスト入力、キーボード処理
ハードもソフトウェア双方