質問をすることでしか得られない、回答やアドバイスがある。

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

ただいまの
回答率

87.35%

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

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,703

score 25

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

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class ItemController : MonoBehaviour
{
    private PlayerController _playerController;
    private ItemGenerator _itemGenerator;
    private ModeManager _modeManager;
    private UIController _uiController;
    private ScoreController _scorecontroller;
    private AudioSource _meron;
    private AudioSource _specialMeron;
    private AudioSource _bad;

    void Start()
    {
        //prefabなのでFind
        _playerController = GameObject.Find("Player").GetComponent<PlayerController>();
        _itemGenerator = GameObject.Find("ItemGenerator").GetComponent<ItemGenerator>();
        _modeManager = GameObject.Find("ModeManager").GetComponent<ModeManager>();
        _uiController = GameObject.Find("UIController").GetComponent<UIController>();
        _scorecontroller = GameObject.Find("ScoreText").GetComponent<ScoreController>();
        AudioSource[] audioSources = GameObject.Find("SE").GetComponents<AudioSource>();
        _meron = audioSources[0];
        _specialMeron = audioSources[1];
        _bad = audioSources[2];
    }

    void FixedUpdate()
    {
        //アイテム落下
        transform.Translate(0, _itemGenerator.Speed * Time.deltaTime, 0);
    }

    void OnTriggerEnter2D(Collider2D collision)
    {
        //床より下に行ったとき
        if (collision.gameObject.tag == "DestroyFloor")
        {
            Destroy(gameObject);
        }

        //床についとき
        if (collision.gameObject.tag == "Floor")
        {
            if (gameObject.tag == "Meron" || gameObject.tag == "SpecialMeron")
            {
                if (!_modeManager.MaxMode && _modeManager.ItemDrop)
                {
                    _bad.PlayOneShot(_bad.clip);
                    _playerController.GameOver = true;
                }
            }
            else
            {
                Destroy(gameObject);
            }
        }

        //プレイヤーに当たったとき
        if (collision.gameObject.tag == "Player")
        {
            if (!_playerController.GameOver)
            {
                if (gameObject.tag == "Meron")
                {
                    _meron.PlayOneShot(_meron.clip);
                    _uiController.PlusScoreSetActiveTrue(10);
                    _scorecontroller.ScorePlus(gameObject);
                    Destroy(gameObject);
                }

                if (gameObject.tag == "SpecialMeron")
                {
                    _specialMeron.PlayOneShot(_specialMeron.clip);
                    _uiController.PlusScoreSetActiveTrue(100);
                    _scorecontroller.ScorePlus(gameObject);
                    _modeManager.MaxMode = true;
                    _modeManager.ItemDrop = false;
                    Destroy(gameObject);
                }
            }

            if (gameObject.tag == "Onigiri")
            {
                _bad.PlayOneShot(_bad.clip);
                _playerController.GameOver = true;
            }
        }
    }
}


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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • hermit19901127

    2019/10/19 02:15

    ちょっと暇つぶしに質問主さんの過去の質問を見てましたが、シングルトンパターンに抵抗があるように見受けられました。

    その場合、Game.Sceneみたいに、シーンのオープンと開始に本ゲームが始まるのであれば
    ・Stage生成時に「StageManager」を生成
    ・StageManagerのStartで、主要6つをFindで取得。StageManagerのメンバ変数とする。
    ・主要6つの相互のやり取りをStageManagerで統括する

    こうするとゲーム終了で、Game.Sceneの終了と同時に
    StageManagerも破棄され、変に生きてる実体も無くなり、すっきりした処理になる思います。

    一番きれいな回答ですが、説明が難しく、言葉足らずなのはご容赦ください…

    キャンセル

回答 1

checkベストアンサー

+1

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

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

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

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

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

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


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

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


追記

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


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

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

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

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る