節電鯖民の遊び場

05-マウスオーバーで表示されるメニューとSave&Load機能をつける

最終更新:

mcforum

- view
メンバー限定 登録/ログイン

目次


できたもの



前提


素材集め

+ 詳細を開く
  • メニューウインドウを作るにあたって、枠を付けてみる
    • 洋風フレーム・装飾 A1 https://commons.nicovideo.jp/material/nc121822
      • 利用許可範囲:インターネット全体に許可
      • 営利利用:営利利用可能
      • その他付加細則あり
      • 素材のフレームのうち、角の部分を切り抜きAssetsに追加しておく。


マウスを載せたら展開するメニューを作る

+ 詳細を開く

パネルの配置

  • Canvas にウインドウになる Panel を配置する。
    • 色を変更する場合は、インスペクターから Image → Color で変更する
  • 画面右側に少しだけ見えていて、マウスを載せたら展開するため左端に素材のフレームを載せる。
    • 一つは Sprite Renderer の設定でY軸反転して載せる。スケールをマイナス値にしても良い。
  • 今後の拡張性を見越して、メニュー内部でスクロールができるよう UI → Scroll View を設置する
  • Scroll View の Viewport → Content にスクロールさせたい文字やボタンを配置する
    • ここでは「めにゅー」の UI → Text (TextMeshPro) と UI → Button(TextMeshPro)

アンカーの設定はお好み

  • Game 画面に見えている状態(パネルを展開した想定の位置)でウインドウサイズを変更してみて挙動を確認
  • ここでの設定は以下
    • Panel は center/middle
    • 縁の素材は left/top と left/bottom
    • Scroll View / Viewport / Content は stretch/stretch
    • "めにゅー" の Text (TMP) は left/top
    • Save Button / Load Button と中の Text(TMP) は stretch/stretch

表示順序の制御

  • カメラに映る表示順序は Canvas や素材 Sprite の Order in Layer の数値で調整する

その他の設定

  • 他にも細かい挙動をインスペクターで設定する
  • Button テキストの Auto Size
  • Scroll View の Scroll Rect → Movement Type でスクロール挙動の調整等

展開/格納アニメーションを作る


  • 続けて格納アニメーションを作成する

  • Panel の Animator で 初期状態を空のState (ここでは Close に名前を変更している) に繋ぎ、その後 展開アニメーション → 格納アニメーション → 展開…となるように矢印を繋ぐ。
    • アニメーションの箱は Assets からドロップで追加できる
  • Close からパネルの展開アニメーションに向かう矢印の設定は、open トリガー契機で、Has Exit Time 及び Fixed Duration のチェックは外し、Transition Duration の値は 0.5 、Interruption Source は Next に設定。

  • パネルの展開アニメーションからパネルの格納アニメーションに向かう矢印の設定は、close トリガー契機で、Has Exit Time 及び Fixed Duration のチェックは外し、Transition Duration の値は 0.5 、Interruption Source は Next に設定。

  • パネルの格納アニメーションからパネルの展開アニメーションに向かう矢印の設定は、open トリガー契機で、Has Exit Time 及び Fixed Duration のチェックは外し、Transition Duration の値は 0.5 、Interruption Source は Next に設定。

パネルにマウスを載せたら展開、パネルからマウスを外したら格納されるスクリプトを設定する

  • パネルに Box Collider 2D を付け、コライダーのサイズをパネルと同じサイズに設定する。
  • マウス検知制御用のスクリプトを付ける。

マウス検知をしてトリガー制御するスクリプト

  • OnMouseEnter と OnMouseExit を実装する。
    • Enter はマウスが乗ったとき、Exit はマウスが離れた時の動作を記述する。
  1. using UnityEngine;
  2.  
  3. public class MenuMouseOver : MonoBehaviour
  4. {
  5. Animator anime;
  6. private void Start() {
  7. anime = gameObject.GetComponent<Animator>();
  8. }
  9. void OnMouseEnter()
  10. {
  11. anime.SetTrigger("open");
  12. }
  13. void OnMouseExit()
  14. {
  15. anime.SetTrigger("close");
  16. }
  17. }
  18.  


セーブとロードのボタンを制御する

メニュー操作をする際には葵ちゃんが増えないようにする

+ 詳細を開く
  • セーブおよびロードをクリックした際に通常のクリック制御が実行されないようにフラグ制御する
  • GameController に通常クリック可否のフラグを設ける
    • 単純なフラグ制御なので、public bool メンバにして直接設定/参照できるようにしておく
      • ここでは public bool clickable とし、初期値は true (クリック可) にする。
  1. public class GameController : MonoBehaviour
  2. {
  3. public static GameController instance;
  4.  
  5. // カウンタの通知を受けるオブジェクトのリスト(インスペクターより登録)
  6. public List<GameObject> counterReceivers;
  7. int clickCount;
  8. public bool clickable;
  9.  
  10. void Awake() {
  11. ~省略~
  12. void Start() {
  13. // オブジェクトを永続化
  14. DontDestroyOnLoad(gameObject);
  15. clickable = true;
  16. }
  17. ~省略~
  18.  

  • メニューを展開した場合には clickable を false に、格納したときは true に制御する。
  1. public class MenuMouseOver : MonoBehaviour
  2. {
  3. Animator anime;
  4. GameController gameCtrl;
  5. private void Start() {
  6. gameCtrl = GameObject.FindObjectOfType<GameController>();
  7. anime = gameObject.GetComponent<Animator>();
  8. }
  9. void OnMouseEnter()
  10. {
  11. anime.SetTrigger("open");
  12. gameCtrl.clickable = false;
  13. }
  14. void OnMouseExit()
  15. {
  16. anime.SetTrigger("close");
  17. gameCtrl.clickable = true;
  18. }
  19. }
  20.  

  • 画面クリック時に、clickable が false の場合には何もしないで return で終了するようにする。
    • これにより、メニューを展開している間は葵ちゃんが増えなくなる
  1. public class Click : MonoBehaviour
  2. {
  3. ~省略~
  4. void Update() {
  5. if (!controller.clickable) {
  6. return;
  7. }
  8. if (Input.GetMouseButtonDown(0)) {
  9. anime.SetTrigger("click_cancel");
  10. anime.SetTrigger("click");
  11. ~省略~
  12.  


カウンタ値を制御する共通処理を実装する

+ 詳細を開く
  • GameController が保持するカウンタ値を設定/取得できる関数を追加する
    • 設定時にはカウンタ値を監視している機能に通知をする必要がある
      • 既存のカウンタ値をインクリメントする機能と共通化するため、通知処理はnotyfyCount関数として分離し、設定やインクリメント時に呼び出すように変更する。
  1. ~省略~
  2. void Start() {
  3. // オブジェクトを永続化
  4. DontDestroyOnLoad(gameObject);
  5. clickable = true;
  6. }
  7. void notifyCount(int count) {
  8. // 指定されたカウント値を、登録オブジェクト全てに通知
  9. foreach (GameObject obj in counterReceivers) {
  10. ICounterReceiver receiver = obj.GetComponent<ICounterReceiver>();
  11. if (receiver != null) {
  12. receiver.UpdateCounter(count);
  13. }
  14. }
  15. }
  16. public void IncrementCount() {
  17. // カウンタをインクリメントして、登録オブジェクト全てに通知
  18. clickCount++;
  19. notifyCount(clickCount);
  20. }
  21. public void SetCount(int count) {
  22. // 設定されたカウンタ値を、登録オブジェクト全てに通知
  23. clickCount = count;
  24. notifyCount(clickCount);
  25. }
  26. public int GetCount() {
  27. // カウンタ値を返却
  28. return clickCount;
  29. }
  30. }
  31.  


セーブの動作を実装する

+ 詳細を開く
  • Save用のスクリプトを作成し、Save用の Button オブジェクトにコンポーネント追加する。
  • Save用のスクリプトを記述する
    • ここでは、簡易な保存方法として PlayerPrefs への保存/読み込みを実施する。
    • セーブを実施した旨は、カウンタ表示用テキストに追記する
  1. using UnityEngine;
  2. using TMPro;
  3.  
  4. public class Save : MonoBehaviour
  5. {
  6. GameController gameCtrl;
  7. TextMeshProUGUI tmp;
  8.  
  9. void Start()
  10. {
  11. gameCtrl = GameObject.FindObjectOfType<GameController>();
  12. tmp = GameObject.FindObjectOfType<CounterTextController>().gameObject.GetComponent<TextMeshProUGUI>();
  13. }
  14. public void OnClick() {
  15. PlayerPrefs.SetInt("Count", gameCtrl.GetCount());
  16. PlayerPrefs.Save();
  17. tmp.text += "(せーぶしました)";
  18. }
  19. }
  20.  

  • ボタンクリックで OnClick() 関数が呼び出されるようにインスペクターで設定する。
    • ボタンのイベントを + ボタンで増やし、オブジェクトとしてヒエラルキーウインドウから Save用の Button をドラッグしてきてドロップする。
    • 呼び出される関数として、Save スクリプトの OnClick() を選択する。


ロードの動作を実装する

+ 詳細を開く
  • 葵ちゃんの数を調整するのは、今後のことも考慮してGameControllerに実施してもらう。
    • GameController に Click クラスを保持するよう修正
    • public の AdjustAoiChan() 関数として葵ちゃんの数を調整する関数を作成
      • 葵ちゃんの数調整と合わせて登録クラスへの通知も実施する(表示も変更される)
  1. using System.Collections.Generic;
  2. using UnityEngine;
  3.  
  4. public class GameController : MonoBehaviour
  5. {
  6. public static GameController instance;
  7.  
  8. // カウンタの通知を受けるオブジェクトのリスト(インスペクターより登録)
  9. public List<GameObject> counterReceivers;
  10. public bool clickable;
  11. int clickCount;
  12. Click click;
  13. ~省略~
  14. void Start() {
  15. // オブジェクトを永続化
  16. DontDestroyOnLoad(gameObject);
  17. clickable = true;
  18. click = GameObject.FindObjectOfType<Click>();
  19. }
  20. ~省略~
  21. public int GetCount() {
  22. // カウンタ値を返却
  23. return clickCount;
  24. }
  25.  
  26. public void AdjustAoiChan(int target) {
  27. // 今いる葵ちゃんの数をカウント
  28. GameObject[] aois = GameObject.FindGameObjectsWithTag("Aoi");
  29. int now = aois.Length;
  30.  
  31. if (now > target) {
  32. // 今いる人数の方が多い場合
  33. while (now > target) {
  34. // 葵ちゃんには消えてもらう
  35. Destroy(aois[now - 1]);
  36. now--;
  37. }
  38. } else {
  39. // 今いる人数の方が少ない場合
  40. while (now < target) {
  41. click.spawnAoi();
  42. now++;
  43. }
  44. }
  45. // 終わったら登録オブジェクトに通知
  46. SetCount(target);
  47. }
  48. }
  49.  

  • Load用のスクリプトを作成し、Load用の Button オブジェクトにコンポーネント追加する。
  • Load用のスクリプトを記述する
    • PlayerPrefs に保存していた情報を読み込み、GameControllerへ葵ちゃんの数調整を依頼。
    • ロードを実施した旨は、カウンタ表示用テキストに追記する
  1. using UnityEngine;
  2. using TMPro;
  3.  
  4. public class Load : MonoBehaviour
  5. {
  6. GameController gameCtrl;
  7. TextMeshProUGUI tmp;
  8. void Start()
  9. {
  10. gameCtrl = GameObject.FindObjectOfType<GameController>();
  11. tmp = GameObject.FindObjectOfType<CounterTextController>().gameObject.GetComponent<TextMeshProUGUI>();
  12. }
  13. public void OnClick() {
  14. // 葵ちゃんの数を調整する
  15. int target = PlayerPrefs.GetInt("Count");
  16. gameCtrl.AdjustAoiChan(target);
  17.  
  18. tmp.text += "(ろーどしました)";
  19. }
  20. }
  21.  



おまけ:リセットボタンとセーブのクリアボタンを実装する

+ 詳細を開く
  • リセットボタンとセーブのクリアボタンを配置する。
    • 設置方法やスクリプトの付け方はセーブやロードと同じ。


  • リセットボタン
  1. using UnityEngine;
  2. using TMPro;
  3.  
  4. public class Reset : MonoBehaviour
  5. {
  6. GameController gameCtrl;
  7. TextMeshProUGUI tmp;
  8. void Start()
  9. {
  10. gameCtrl = GameObject.FindObjectOfType<GameController>();
  11. tmp = GameObject.FindObjectOfType<CounterTextController>().gameObject.GetComponent<TextMeshProUGUI>();
  12. }
  13. public void OnClick() {
  14. // 葵ちゃんの数をリセットする
  15. gameCtrl.AdjustAoiChan(0);
  16.  
  17. tmp.text += "(りせっとしました)";
  18. }
  19. }
  20.  

  • セーブのクリアボタン
  1. using UnityEngine;
  2. using TMPro;
  3.  
  4. public class Clear : MonoBehaviour
  5. {
  6. TextMeshProUGUI tmp;
  7.  
  8. void Start()
  9. {
  10. tmp = GameObject.FindObjectOfType<CounterTextController>().gameObject.GetComponent<TextMeshProUGUI>();
  11. }
  12. public void OnClick() {
  13. PlayerPrefs.DeleteKey("Count");
  14. tmp.text += "(せーぶをクリアしました)";
  15. }
  16. }
  17.  

目安箱バナー