🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

3256閲覧

PrefabにアタッチしたスクリプトでFind関数を使うことについて

kanata_02

総合スコア25

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2019/10/18 14:42

現在Unityにてキャラクターを移動させ、落ちてきたアイテムを拾いスコアを稼ぐゲームを作成しています。

落ちてくるアイテムはプレハブ化したものをインスタンス化して使用していて、以下のスクリプトをアタッチしているのですが

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4 5public class ItemController : MonoBehaviour 6{ 7 private PlayerController _playerController; 8 private ItemGenerator _itemGenerator; 9 private ModeManager _modeManager; 10 private UIController _uiController; 11 private ScoreController _scorecontroller; 12 private AudioSource _meron; 13 private AudioSource _specialMeron; 14 private AudioSource _bad; 15 16 void Start() 17 { 18 //prefabなのでFind 19 _playerController = GameObject.Find("Player").GetComponent<PlayerController>(); 20 _itemGenerator = GameObject.Find("ItemGenerator").GetComponent<ItemGenerator>(); 21 _modeManager = GameObject.Find("ModeManager").GetComponent<ModeManager>(); 22 _uiController = GameObject.Find("UIController").GetComponent<UIController>(); 23 _scorecontroller = GameObject.Find("ScoreText").GetComponent<ScoreController>(); 24 AudioSource[] audioSources = GameObject.Find("SE").GetComponents<AudioSource>(); 25 _meron = audioSources[0]; 26 _specialMeron = audioSources[1]; 27 _bad = audioSources[2]; 28 } 29 30 void FixedUpdate() 31 { 32 //アイテム落下 33 transform.Translate(0, _itemGenerator.Speed * Time.deltaTime, 0); 34 } 35 36 void OnTriggerEnter2D(Collider2D collision) 37 { 38 //床より下に行ったとき 39 if (collision.gameObject.tag == "DestroyFloor") 40 { 41 Destroy(gameObject); 42 } 43 44 //床についとき 45 if (collision.gameObject.tag == "Floor") 46 { 47 if (gameObject.tag == "Meron" || gameObject.tag == "SpecialMeron") 48 { 49 if (!_modeManager.MaxMode && _modeManager.ItemDrop) 50 { 51 _bad.PlayOneShot(_bad.clip); 52 _playerController.GameOver = true; 53 } 54 } 55 else 56 { 57 Destroy(gameObject); 58 } 59 } 60 61 //プレイヤーに当たったとき 62 if (collision.gameObject.tag == "Player") 63 { 64 if (!_playerController.GameOver) 65 { 66 if (gameObject.tag == "Meron") 67 { 68 _meron.PlayOneShot(_meron.clip); 69 _uiController.PlusScoreSetActiveTrue(10); 70 _scorecontroller.ScorePlus(gameObject); 71 Destroy(gameObject); 72 } 73 74 if (gameObject.tag == "SpecialMeron") 75 { 76 _specialMeron.PlayOneShot(_specialMeron.clip); 77 _uiController.PlusScoreSetActiveTrue(100); 78 _scorecontroller.ScorePlus(gameObject); 79 _modeManager.MaxMode = true; 80 _modeManager.ItemDrop = false; 81 Destroy(gameObject); 82 } 83 } 84 85 if (gameObject.tag == "Onigiri") 86 { 87 _bad.PlayOneShot(_bad.clip); 88 _playerController.GameOver = true; 89 } 90 } 91 } 92} 93

毎回FindでほかのManagerがアタッチされたオブジェクトを探しており、メモリに負荷をかける原因になるのではないだろうかと思いました。
Publicで登録するなどにしたいのですがPrefabなのでそれもできず、どのようにすればいいか悩んでおります。
Findで検索する以外にどのようなやり方がありますでしょうか?何かアドバイスいただけますと幸いです。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hermit19901127

2019/10/18 17:15

ちょっと暇つぶしに質問主さんの過去の質問を見てましたが、シングルトンパターンに抵抗があるように見受けられました。 その場合、Game.Sceneみたいに、シーンのオープンと開始に本ゲームが始まるのであれば ・Stage生成時に「StageManager」を生成 ・StageManagerのStartで、主要6つをFindで取得。StageManagerのメンバ変数とする。 ・主要6つの相互のやり取りをStageManagerで統括する こうするとゲーム終了で、Game.Sceneの終了と同時に StageManagerも破棄され、変に生きてる実体も無くなり、すっきりした処理になる思います。 一番きれいな回答ですが、説明が難しく、言葉足らずなのはご容赦ください…
guest

回答1

0

ベストアンサー

質問主様のおっしゃる通り、Findは重く、Update処理等では使わない方が賢明です。
ですが、Startのような、1フレームで終わるような処理であれば、似たような処理を行うものがゲーム中に100個も200個も作られるようなことが無い限りは、致命的なフレーム落ち等は起こらない…ですが、

いちいちFindで参照を持たず、「どのクラスからでもアクセスが簡単に行える機構が欲しい」となれば
シングルトンパターンを使うのもありかと思います。
https://unity.moon-bear.com/unitychan-coin/singleton-gamemanager/

このシングルトンパターンで作ったクラスは、特に前処理も必要なく、ほぼすべてのクラスが自由にアクセス可能となります。

そのシングルトンクラスに

C#

1 _playerController = GameObject.Find("Player").GetComponent<PlayerController>(); 2 _itemGenerator = GameObject.Find("ItemGenerator").GetComponent<ItemGenerator>(); 3 _modeManager = GameObject.Find("ModeManager").GetComponent<ModeManager>(); 4 _uiController = GameObject.Find("UIController").GetComponent<UIController>(); 5 _scorecontroller = GameObject.Find("ScoreText").GetComponent<ScoreController>(); 6 AudioSource[] audioSources = GameObject.Find("SE").GetComponents<AudioSource>();

シングルトンの初期化タイミングや、これらが生成されたタイミングでシングルトンクラスに代入すれば、
それ以降、どのクラスからも自由に上記のクラスへアクセスが可能です。


ここまでは聞こえがよく、凄く強い機能に見えますが、諸刃の剣のようなもので
「どこからでも書き換えられる」事は「自分が修正なりして消したはずの所から急に書き換えられる」等で
特定し辛いエラーの原因にもなったりするので、運用には気を付けてください。

それでも、シーンの切替等が発生しても値を保持できる点で、ゲーム中のデータ保存の方法としては強力だとは思います…


追記

これは、古い知識かもしれませんが、Unityのプロジェクトセッティングか、それに類するものに
「シングルトンオブジェクト」を専用に登録できるメニュー等があった気がします…
余力があったら、探してみるのも一考…


与太話(聞き流しても大丈夫です)

普通に作れば大丈夫ですが、シングルトンオブジェクト、下手な実装をすると
ゲームを終了してもシングルトンオブジェクトが生きて、再度ゲームを実行すると
前のゲームのデータを保持したまま実行するみたいなトラブルが起こる事もあります…。

実は、UnityEditor自体が、一つのゲームのプログラムになっていて、
そこで再生してるゲームは、そのプログラムが再生してる現象でしかありません…
static関数をどう管理するかにもよりますが、やり方次第で、
「UnityEditorが起動中は存在し続ける」実装も可能なので、
運悪く、そんな実装をすると、いくら再生してるゲームを終了しようが
UnityEditorそのものを終了しないと初期化処理が走らず、前のゲームのデータを引き継いだまま処理を行う、のような、珍妙な動作が起こります…。

…まぁ、エディタ再生の終了、またはエディタ再生を感知して、シングルトンを常に初期化するようにすれば、そのような問題は起きませんが…

一応、知見というか、豆知識として…

投稿2019/10/18 15:13

編集2019/10/18 16:35
hermit19901127

総合スコア368

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問