節電鯖民の遊び場

06-リファクタリング(ソースコードとオブジェクト関係の整理)

最終更新:

mcforum

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

目次


前提


概要



  • 今後の拡張を見据えて、少し雑になっていたオブジェクト関係を見直し
  • それに伴いスクリプトの内容も見直し
    • 茜ちゃん関連の操作は、トップレベルにいる AkaneController オブジェクトに実行させる
    • 葵ちゃん関連の操作は、トップレベルにいる AoiController オブジェクトに実行させる
    • AkaneController/AoiController は GameController からのカウント変更通知を受けて動作させる

スクリプトを見直す

ICounterReceiver

  • 変更無し
+ 詳細を開く
  1. public interface ICounterReceiver
  2. {
  3. public void UpdateCounter(int count);
  4. }
  5.  

Click

  • クリックしたら GameController にインクリメントを依頼するだけの処理に変更
  • 茜ちゃんの Click スクリプトコンポーネントは削除し、GameController オブジェクトに Click スクリプトを付け替え。
+ 詳細を開く
  1. using UnityEngine;
  2.  
  3. public class Click : MonoBehaviour
  4. {
  5. GameController gameCtrl;
  6.  
  7. void Start() {
  8. gameCtrl = GameObject.FindObjectOfType<GameController>();
  9. }
  10. void Update() {
  11. if (!gameCtrl.clickable) {
  12. return;
  13. }
  14. if (Input.GetMouseButtonDown(0)) {
  15. // クリック数をインクリメントし、ゲームコントローラー経由で全体に通知する
  16. gameCtrl.IncrementCount();
  17. }
  18. }
  19. }
  20.  

GameController

  • 本クラスで実施していた葵ちゃん制御関連の関数は、AoiController に移動
  • AoiController/AkaneController は本オブジェクトの counterReceivers にインスペクターから登録するよう変更
+ 詳細を開く
  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 Awake() {
  15. // シーンを跨いでGameControllerがいた場合用にシングルトン実装
  16. if (instance == null) { instance = this; }
  17. else { Destroy(gameObject); }
  18. }
  19. void Start() {
  20. // オブジェクトを永続化
  21. DontDestroyOnLoad(gameObject);
  22. clickable = true;
  23. click = GameObject.FindObjectOfType<Click>();
  24. }
  25. void notifyCount(int count) {
  26. // 指定されたカウント値を、登録オブジェクト全てに通知
  27. foreach (GameObject obj in counterReceivers) {
  28. ICounterReceiver receiver = obj.GetComponent<ICounterReceiver>();
  29. if (receiver != null) {
  30. receiver.UpdateCounter(count);
  31. }
  32. }
  33. }
  34. public void IncrementCount() {
  35. // カウンタをインクリメントして、登録オブジェクト全てに通知
  36. clickCount++;
  37. notifyCount(clickCount);
  38. }
  39. public void SetCount(int count) {
  40. // 設定されたカウンタ値を、登録オブジェクト全てに通知
  41. clickCount = count;
  42. notifyCount(clickCount);
  43. }
  44. public int GetCount() {
  45. // カウンタ値を返却
  46. return clickCount;
  47. }
  48. }
  49.  

AkaneController

  • 茜ちゃん制御用のクラスを新規作成
  • Clickで実施していたアニメーションの制御を本クラスに移動し、UpdateCounter(カウンタ変更通知)で実施するよう変更
+ 詳細を開く
  1. using UnityEngine;
  2.  
  3. public class AkaneController : MonoBehaviour, ICounterReceiver
  4. {
  5. public GameObject akane;
  6. Animator anime;
  7.  
  8. void Start()
  9. {
  10. anime = akane.GetComponent<Animator>();
  11. }
  12. public void UpdateCounter(int count) {
  13. // 数値変動に応じてアニメーションする
  14. anime.SetTrigger("click_cancel");
  15. anime.SetTrigger("click");
  16. }
  17. }
  18.  

AoiController

  • 葵ちゃんを生成する処理及び数の調整用関数を本クラスに移動
  • それに伴い葵ちゃんを生成する位置は、外部から GameObject を登録する事で同座標に出現させるよう変更
  • 葵ちゃんは AoiController 配下に生成されるよう変更し、検索も AoiController 配下のオブジェクトをカウントするよう変更
+ 詳細を開く
  1. using UnityEngine;
  2.  
  3. public class AoiController : MonoBehaviour, ICounterReceiver
  4. {
  5. public GameObject prefab; // 生成するprefab を簡単に差し替えできるよう public で外部から変更できるようにしておく
  6. public GameObject posTarget; // 葵ちゃんを出現させる地点をオブジェクトで指定する
  7.  
  8. // 速度は適切な速度を探れるように、publicで外部から変更できるようにしておく
  9. public float prefabSpeed = 1000;
  10. public float prefabTorque = 300;
  11.  
  12. public void UpdateCounter(int count) {
  13. // 数値変動に応じて葵ちゃんの数を増減させる
  14. AdjustAoiChan(count);
  15. }
  16. public void spawnAoi() {
  17. // prefab からインスタンスを生成
  18. GameObject aoi = Instantiate(prefab, posTarget.transform.position, Quaternion.identity);
  19. Rigidbody2D aoiBody = aoi.GetComponent<Rigidbody2D>();
  20.  
  21. // 葵ちゃんの親を AoiController に設定する
  22. aoi.transform.SetParent(gameObject.transform);
  23.  
  24. // 上方向のランダムな方向を決定
  25. Vector2 direction = Random.insideUnitCircle.normalized;
  26. direction.y = Mathf.Abs(direction.y);
  27.  
  28. // ランダムな回転速度を決定、整数にしたとき2で割り切れない場合は逆回転にする
  29. float torque = Random.value;
  30. if ((int)(torque*100) % 2 == 1) {
  31. torque *= -1;
  32. }
  33.  
  34. // 生み出した葵ちゃんに力を与える
  35. aoiBody.AddForce(direction * prefabSpeed, ForceMode2D.Force);
  36. aoiBody.AddTorque(torque * prefabTorque, ForceMode2D.Force);
  37. }
  38. void AdjustAoiChan(int target) {
  39. // 今いる葵ちゃんの数をカウント
  40. int now = transform.childCount;
  41.  
  42. // 今いる人数の方が多い場合は葵ちゃんには消えてもらう
  43. foreach (Transform aoi in transform) {
  44. if (now <= target) {
  45. break;
  46. }
  47. Destroy(aoi.gameObject);
  48. now--;
  49. }
  50. // 今いる人数の方が少ない場合は葵ちゃんを生み出す
  51. while (now < target) {
  52. spawnAoi();
  53. now++;
  54. }
  55. }
  56. }
  57.  

CounterTextController

  • 変更無し
+ 詳細を開く
  1. using UnityEngine;
  2. using TMPro;
  3.  
  4. public class CounterTextController : MonoBehaviour, ICounterReceiver
  5. {
  6. TextMeshProUGUI tmp;
  7.  
  8. void Start()
  9. {
  10. tmp = gameObject.GetComponent<TextMeshProUGUI>();
  11. }
  12. public void UpdateCounter(int count) {
  13. tmp.text = count + " あおい~";
  14. }
  15. }
  16.  

MenuMouseOver

  • 変更無し
+ 詳細を開く
  1. using UnityEngine;
  2.  
  3. public class MenuMouseOver : MonoBehaviour
  4. {
  5. Animator anime;
  6. GameController gameCtrl;
  7. private void Start() {
  8. gameCtrl = GameObject.FindObjectOfType<GameController>();
  9. anime = gameObject.GetComponent<Animator>();
  10. }
  11. void OnMouseEnter()
  12. {
  13. anime.SetTrigger("open");
  14. gameCtrl.clickable = false;
  15. }
  16. void OnMouseExit()
  17. {
  18. anime.SetTrigger("close");
  19. gameCtrl.clickable = true;
  20. }
  21. }
  22.  

TriggerDestroy

  • 葵ちゃん削除用の壁の名前を "Dead" で始まるように変更し、削除用の壁に触れたときのみ削除するよう判定を修正
    • これによりメニューパネルの Collider で IsTrigger 設定を有効(接触判定を無効)にしても誤削除がされなくなる
+ 詳細を開く
  1. using UnityEngine;
  2.  
  3. public class TriggerDestroy : MonoBehaviour
  4. {
  5. void OnTriggerEnter2D(Collider2D other) {
  6. if (other.name.StartsWith("Dead")) {
  7. Destroy(gameObject);
  8. }
  9. }
  10. }
  11.  

Save

  • 変更無し
+ 詳細を開く
  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.  

Load

  • GameController に最新の数値を通知するのみの処理に変更 (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.SetCount(target);
  17. tmp.text += "(ろーどしました)";
  18. }
  19. }
  20.  

Reset

  • GameController にカウンタ 0 を通知するのみの処理に変更 (GameController による全体へのカウンタ更新通知で反映される)
+ 詳細を開く
  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.SetCount(0);
  16. tmp.text += "(りせっとしました)";
  17. }
  18. }
  19.  

Clear

  • 変更無し
+ 詳細を開く
  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.  

オブジェクトの関連付け等を見直す

オブジェクト名の変更と位置変更

  • 葵ちゃんを削除する機能を持った壁類はDeadで始まるように変更
    • それ以外は任意
  • AkaneController/AoiController を追加
  • 茜オブジェクトは AkaneController 配下に変更 ※葵ちゃんもAoiController配下に作成される
+ 詳細を開く

各種設定変更


パネルと葵ちゃんの接触判定を削除 + メニューマウスオーバーが葵ちゃんを誤検出しないよう修正

  • IsTriggerの有効化
    • これによりメニューと葵ちゃんが接触しなくなるよう変更
  • 葵ちゃんのPrefabを Ignore Raycast レイヤーに変更
    • これによりメニューと重なってもマウスオーバーを誤検出しなくなる
+ 詳細を開く


GameController のスクリプト設定

  • リストに AkaneController と AoiController のオブジェクトを追加
    • これにより茜ちゃん葵ちゃん関連の処理は、カウンタ値の更新通知で駆動するように変更
  • 茜ちゃんに付けていた Click スクリプトを剥がして本オブジェクトに付け替え(茜ちゃんと言うより画面のクリックなので)
+ 詳細を開く

AoiController にクリック時に出現させる葵ちゃんのPrefabと出現位置を登録

+ 詳細を開く

AkaneController に茜ちゃんのオブジェクトを登録

  • 配下にいるので検索できなくも無いが処理削減
+ 詳細を開く
目安箱バナー