前回は使用するリソース(画像・フォント)のインポートや、ゲームサイズの設定、描画レイヤーの追加などの準備を行いました。
前回の記事↓
第2回である今回はUI等の画面配置と羊オブジェクトの作成をやっていきます。
ではやっていきましょう。
クリッカーゲームのUI画面作成
まず、uguiをシーンに追加していきます。
ヒエラルキービューを右クリックし(何も選択されていない状態、MainCameraを選択しないよう注意)UI→Panelを選択し、Panelを作成します。
Panelの設定
作られたPanelを設定していきます。 (なお、実際には「Panel」というコンポーネントは存在しません。uguiで何かを配置するための土台のテンプレートとして半透明のImageコンポーネントが付いたオブジェクトが作られるだけです。)
最初に作ったこのPanelは名前を「Panel(Header)」に変更し、AnchorPresetsから、top strechを選択(Shift+Altキーを押しながら、右上のボタン)し、AnchorとPivotを設定します。
そして、Heightは50にしておきます。
この、作成した「Panel(Header)」を2回複製(CTRL+D)して、「Panel(Sell)」と「Panel(Shop)」にそれぞれ名前を変更します。
それぞれ RectTransformの位置、幅を以下のように設定します。
Panel(Sell)
- Pos Y : -530
- Height : 70
Panel(Shop)
- Pos Y : -600
- Height : 200
正しく設定すると以下のような画面、対応になります。
背景画像の配置
次に、ゲーム画面の背景を配置します。
Assets/Images/ から 背景用の画像 bg をHierarchyに直接ドラッグアンドドロップします。
Transform の Position は 最初から全て0になった状態だとは思いますが、もし数値が入っているようでしたら、全て0にしておいてください(…からResetを選択しても良いですね)。
描画順を示す Sorting Layer はDefault から BG に変更しておきます。
(このSortingLayerの追加は前回行っています。 もし、ドロップダウンにDefaultしかないなど、下の画像と差が出ている場合は前回の記事を見直して修正してください)
この時点でゲーム画面はこのようになります。
先ほど用意したuguiのPanelと背景画像(bg)が被ってしまっていますね。
被らないよう位置を調整しなくてはいけないのですが、その前に初学者がハマりがちな以下の状況についての説明と対処方法を教えておきたいと思います。
SceneとGameで表示が異なるときの調整
Hierarchyで今配置したbgをダブルクリック(またはbgを選択した状態で メニューバーの GameObject → Align View to Selectedを選択)して、bgにフォーカスを当ててみてください。
背景画像は表示されますが、UIが見当たりませんね。 そして謎の右上のボヤボヤ。
逆に、HierarchyのCanvasにフォーカスを当てると(ダブルクリックなど)
UIは表示されますが、bg(背景)はどこに・・・?という状態になります。
実はUIの左下に小さく映っているの(画像、矢印の先)がbg(背景)なんですね。なんということでしょう。
Canvasのモード変更 Render Modeの使い方
これは、Canvas(ugui)の仕組みに起因するものです。
今、Hierarcyに自動で作られたCanvasを選択すると、Inspectorは以下のようになっていると思います。
注目するのは、CanvasコンポーネントのRender Mode という設定です。
ここが最初は「Screen Space – Overlay」となっています。
UIは基本的にはゲーム画面とは独立して、一番手前に表示される(なにかゲームの画面で隠れたりしない)事を想定されています(でないと、ユーザーが触れませんからね)
そのためUnityの基本設定では、私たちゲーム制作者でも触れない「特別なカメラ」を用意して、必ず一番最後に描画するようになっています。
別々のカメラで撮った画像を最後に重ねているので、SceneではサイズがバラバラでもGame画面では重なって見えているというわけです(実際の街の写真に、20cmぐらいの怪獣のフィギュアの写真を上手く重ねてビルより大きく見えるようにしているようなイメージです)
もちろん、このままでもゲームは作れるのですが、SceneビューとGameビューの見た目が一致していた方が何かと都合が良いので、今回はこの「Render Mode」をScreen Space – OverlayからScreen Space – Camera に変更します。
すると、Render Camera という「どのカメラでCanvasを描画するか」が指定できるようになりました。(一緒に警告が表示されてますね。 ざっくり読むと、「カメラがセットされてないから、Overlayと一緒だよ」という感じでしょうか。)
では、この Render Camera に、Hierarchyから Main Camera をドラッグアンドドロップでセットします。
すると、Inspectorが少し変化し、先ほどのbgのSpriteRendererの設定と同じように 描画順を示す Sorting Layerが設定できるようになります。これは使用するカメラが同じになったので、描画順をセットする必要が出たということです。
Sorting Layer は 「UI」にしておきましょう。
試しに、SortingLayerをBGにしてみると、背景画像よりもUIが後ろになることがわかります。
UIが後ろに配置される事にあまり使いどころはありませんが、「背景画像もuguiで描画したい」や、「パーティクル・エフェクトはUIにも重なって欲しい」といった時に効力を発揮します。
背景画像の配置
それでは、改めて背景の画像の位置を修正しましょう。
Transform の Position Y は 1.4 にしておきましょう(SceneビューでもUI(Canvas)とサイズが一致して1画面内に収まるようになっているので、直接マウスで位置を調整してもよいです)。
羊オブジェクトとスクリプトの作成
ざっくりとですが画面配置ができましたので、次に羊(Sheep)オブジェクトを作成していきます。
Assets/Images/ から 羊用の画像 sheep をSceneに直接ドラッグアンドドロップします。
sheepオブジェクトが作成されますので、Inspectorから新規スクリプトもAddComponentします。 スクリプト名は「Sheep」にしましょう。
羊(Sheep)オブジェクトの画像変更処理と当たり判定の作成
まず、羊をクリックするゲームなので、羊をマウスで触れるようにする必要があります。
そしてクリックされた事が目で見えた方が良いので、クリックしたら羊の画像を、「カット済みの羊」の画像に差し替えるようにしましょう。
では、Sheepスクリプトを以下のように修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
using System.Collections; using System.Collections.Generic; using UnityEngine; public class Sheep : MonoBehaviour { [SerializeField] private SpriteRenderer sheepRenderer; [SerializeField] private Sprite cutSheepSprite; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void Shaving() { sheepRenderer.sprite = cutSheepSprite; } private void OnMouseOver() { if (Input.GetMouseButton(0) == false) return; Shaving(); } } |
まず、Inspectorからセット出来るように [SerializeField]
属性で、
7 8 9 10 |
[SerializeField] private SpriteRenderer sheepRenderer; [SerializeField] private Sprite cutSheepSprite; |
羊の画像を描画している SpriteRenderer sheepRenderer
と、差し替え用の画像である Sprite cutSheepSprite
をメンバ変数に用意しています。
次に、刈られた時の処理として Shaving() 関数を作っています。
22 23 24 25 |
private void Shaving() { sheepRenderer.sprite = cutSheepSprite; } |
内容としては、画像の差し替え(cutSheepSprite)です。
そして、実際にマウスクリックされた時の処理、
なのですが、ここでは OnMouseOver
という関数を使っています。
ここが要注意(Unityのあまり良くない所)なのですが、この「OnMouseOver」という関数は、一字一句、大文字小文字も含め間違えず「OnMouseOver」と書く必要があります。
これは、スクリプトを新規作成した時に書いてある Start()
や Update()
と同じで、決められたルールでUnity側から呼ばれる関数になります。
最も基本的なものは以下の3つ
Awake()
オブジェクトが生成され、一番最初に1回だけ呼ばれるStart()
一番最初のUpdateが呼ばれる前に1回だけ呼ばれるUpdate()
毎フレーム呼ばれる
になりますが、その他にも多くの関数が用意されています。
そのうちの一つが今回用意した関数 OnMouseOver
になります。
これは、読んで字のごとく、マウスが重なる(Over)すると呼ばれる関数になります。
27 28 29 30 31 |
private void OnMouseOver() { if (Input.GetMouseButton(0) == false) return; Shaving(); } |
関数内では刈られた時の処理として Shaving() を呼んでいますが、その前に
if文でInput.GetMouseButton(0)
が false
だった場合に何もせずreturnしています。
OnMouseOverなのでマウスが重なるだけで呼ばれますが、マウスボタンが押されていなければreturn(抜ける) なので
「マウスを押したままでカーソルが重なった時だけShaving()を呼ぶ。」
という処理になりました。
これはゲームをプレイするユーザーの負担を減らすためです(ずっとカチカチクリックするの辛いですよね)
もし「いやいやいや、『クリッカーゲーム』なんだから、クリックしてなきゃダメだよ!」という方は、Input.GetMouseButton(0)
を Input.GetMouseButtonDown(0)
に変更してください。
ここで、「マウスが押されていたらShaving()を呼ぶ」のであれば
1 2 3 4 5 6 7 |
private void OnMouseOver() { if (Input.GetMouseButton(0)) { Shaving(); } } |
では?と思った方もおられるでしょう。
これは好みの部分もあるのですが、ネストが深くなるのを避ける一つの手法です(俗に『早期リターン』と呼ばれます)
ネストが深い というのは、{ }
の入れ子が複数重なって、}
を見たときに、どの処理に対する }
なのかが分からないような、{
と }
の対応が分かりづらくなっている状態を言います。
細かい状況にもよりますが、if文を重ねて条件にあった場合の処理を書くより、条件に合わない場合に早々に処理を切り上げる(returnしてしまう)方がネストが浅くなり、結果可読性が上がります。
ちょっと意識してみてはいかがでしょうか。
では、スクリプトを保存して、UnityEditorに戻ります
Sheepオブジェクトを選択してInspectorの SheepRenderer (sheepオブジェクトに割り当てられているSpriteRenderer)と Cut Sheep Sprite (Assets/Images/sheep_cut) をそれぞれセットします。
それともう一つ、OnMouseOver
などのマウスとの当たり判定を取る処理には Collider を付ける必要があります。
Add Component からPhysics 2D → Polygon Collider 2D を選択して、追加します。
(虫眼鏡マークの横に “Polygon” と入れて絞りこんで追加しても良いです)
しかし、物理演算的に何かとぶつかって欲しいわけではないので、Polygon Collider 2D の IsTrigger にチェックを入れておきます。
「当たり判定」とよく言いますが、Unityでは大きく2つ、そしてそれぞれ2つに分かれます。
まず
- 2D用
- 3D用
これは、見たまま2D用の当たり判定か、3D用の当たり判定か。 です。
Unityの歴史上、当初は2D用の当たり判定は存在しませんでした。(もちろんその当時から2Dのゲームも作れましたが当たり判定は3Dでやっていました。大は小を兼ねるというやつですね)
後から2D用の当たり判定が追加されています。 そういった経緯から、PolygonCollider2D のように 2Dと付いているものが2D用、ついていないものは3D用です。
次に、「当たった時の挙動」が
- 当たった時に物理挙動をする(当たった二つは衝突する)
- 当たっても物理挙動をしない(当たった事だけ通知する)
の2つです。
「当たり判定」なので当然他のオブジェクト等と当たったかどうかは通知して欲しいんですが、物理的に衝突して欲しいかどうかは別物です。
例えば対戦型徒競走ゲームを作っていて、ゴールテープのオブジェクト(あのゴール前に貼られている布です)を置いたとします。
そのゴールテープに先に触れたプレイヤーが勝者としたときに、先頭を走るプレイヤーがゴールテープにぶつかって前に進めなくなっていては困るわけです(走り抜けて欲しいですよね)
そんな時に「そのあたり判定は物理挙動をしない、通知を送るだけのものですよ」という意味が「IsTrigger」になります。
(トリガー=銃などの引き金 → 転じて 当たった判定を通知する引き金 という意味です)
さて、これでOnMouseOver
が呼ばれるようになりました。 UnityEditor上で実行して動きを確かめてみましょう。
羊の毛が刈られて裸になればOKです。
羊(Sheep)オブジェクトのPrefab化
今回作った羊(Sheep)オブジェクトは、ゲームに何匹も出てくることになり、使いまわす前提なのでprefab化をしておきましょう。
Hierarchyからsheepオブジェクトを Assets/Prefabs にドラッグアンドドロップしてprefab化しておきます。
おさらいと次回予告
今回は画面配置と羊オブジェクトを作成しました。
次回は、羊から刈り取る「羊毛」の作成をやっていきます。
次回の記事↓
コメント
ゲーム作成参考にさせてもらっております。
unity初心者です。
大変初歩的な質問になりますが、
以下の記述について
if (Input.GetMouseButton(0) == false) return; //左クリック押されていなければ抜ける
Shaving(); //押されていれば関数を実行する。
上記、コメントの認識ですが
”左クリックが押されている”という
認識はどこでおこなっているのでしょうか。
てっきり、else if(Input.GetMouseButton(0) == true)という
記述が必要なのかと想定しておりました。
if (Input.GetMouseButton(0) == false) return;
↑こちらでボタンが押されていなければ処理を中断しています。
逆に言うとボタンが押されていれば、処理を中断せず、
Shaving();
が実行されます。押されているの確認は不要ということですね。押されてない場合以外=押されているときの処理がShaving(); なので。
else if(Input.GetMouseButton(0) == true)
を書く形にしてもいいのですが、これだとコードが長くなる&条件文もその先のコードもいちいち読んでいかないといけません。
一方、先にreturn文があれば「もうこのあとの処理は実行されないんだな」ということが
if (Input.GetMouseButton(0) == false) return;
一つあるだけで誰の目にも明らかな記述になります。
はじめまして。講座購入させていただいた初心者です。Panel作成の部分で、下記の記載があるのですが『最初に作ったこのPanelは名前を「Panel(Header)」に変更し、AnchorPresetsから、top strechを選択(Shift+Altキーを押しながら、右上のボタン)し、AnchorとPivotを設定します。』
ShiftとAltを押すと画面はわかるのですが、決定などできません。
ここは具体的にどのような操作をすればよいのでしょうか??
初歩的なことで大変申し訳ございません。
”AnchorPresetsから、top strechを選択(Shift+Altキーを押しながら、右上のボタン)し、AnchorとPivotを設定します。”
↑まさにこの部分の下にある画像の操作を行うだけです。
Shift+Altキーを押してマウスで一度右上をクリックしたらOKです。それで設定されてるはずです。