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

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

ただいまの
回答率

88.91%

【Unity】実行ボタンを押すとフリーズしてしまう

解決済

回答 2

投稿

  • 評価
  • クリップ 2
  • VIEW 13K+

intenseG

score 34

前提・実現したいこと

囲碁の詰碁問題(将棋でいうところの詰将棋)を出題するアプリを制作中なのですが、ある日、Unityで実行ボタンを押した途端メモリ使用率が97%以上に跳ね上がり、パソコンがフリーズしてしまいました。

そこで、グーグル大先生にお尋ねしてみたところ「無限ループしてる」「処理が重い」というのが原因の候補にあがってきたのですが、、

まず、無限ループしている箇所がないかを確認しましたが、ありませんでした。
となると「処理が重い」というのが原因だと思うのですが、どの箇所の処理が重いのかが全く検討もつかず途方に暮れています。

Unity画面

発生している問題

実行するとメモリ使用率が97%以上に跳ね上がり、フリーズしてしまいます。

ソースコード

public class TsumegoManager : MonoBehaviour
{
    int panelCountX = 0;    //横の石数
    int panelCountY = 0;    //縦の石数
    public GameObject stonePrefab; //石のひな型

    public GameObject panelBasePosi;  //生成した石を置く親オブジェクト
    List<StoneManager> stoneList;     //生成した石のスクリプトリスト

    List<List<int>> correctArray;   //正解配置格納リスト(複数)
    List<int> selectArray;  //選択配置格納リスト(偶数黒 奇数白)
    int pushIndex = 0;  //選択インデックス(偶数黒 奇数白)

    public Image correctImg;    //正解画像
    public Image wrongImg;    //不正解画像
    public Image blockImg;      //画面タップ防止用画像

    void Start()
    {
        //石のリストと配置リストを初期化
        stoneList = new List<StoneManager>();
        selectArray = new List<int>();

        //配置する石数
        panelCountX = 4;
        panelCountY = 8;

        //ステージの問題(左上からの石の並びを1次元配列で表現)
        string stageInfo = "0,0,0,0,2,2,2,2,2,1,1,0,2,1,0,0,2,0,1,0,0,2,1,0,0,2,1,0,0,2,2,2,0,0,0,0";
        //カンマで分割
        string[] stageArray = stageInfo.Split(',');

        //正解用配列を生成
        correctArray = new List<List<int>>();
        //解答手順をあるだけ生成して格納
        List<int> correct1 = new List<int> { 104, 143, 130 };
        correctArray.Add(correct1);

        //1パネルのサイズ
        float panelWid //元は60.0fth = 53;

        //一番左上のポジション
        float panelInitPosiX = 0.0f;
        float panelInitPosiY = 0.0f;
        panelInitPosiX = -1.0f * panelWidth * (panelCountX / 2.0f - 0.5f);
        panelInitPosiY = 1.0f * panelWidth * (panelCountY / 2.0f - 0.5f);
        Debug.Log(panelInitPosiX);
        Debug.Log(panelInitPosiY);

        //石の配置(左上から右上 一段下がって左からの順序)
        //縦
        for (int i = 0; i < panelCountY; i++)
        {
            //横
            for (int j = 0; j < panelCountX; j++)
            {
                //石を生成
                GameObject obj = (GameObject)Instantiate(stonePrefab);
                //親オブジェクトを設定
                obj.transform.parent = stonePrefab.transform;
                //拡大率を1に設定
                obj.transform.localScale = Vector3.one;
                //ポジションを設定
                obj.transform.localPosition = new Vector3(panelInitPosiX + panelWidth * j, panelInitPosiY - panelWidth * i, 0);

                //スクリプトを取得してリストに追加
                StoneManager stoneManager = obj.GetComponent<StoneManager>();
                stoneList.Add(stoneManager);

                //インデックスを算出
                int index = i * panelCountX + j;

                //ボタンの名前をインデックスにする
                //ボタン押したときに使います。
                stoneManager.komaBtn.gameObject.name = index.ToString();

                //石の設定                
                if (int.Parse(stageArray[index]) == 0)
                {
                    //石なし タップするボタンが活性
                    stoneManager.setKomaInfo("NONE");
                }
                else if (int.Parse(stageArray[index]) == 1)
                {
                    //白い石 ボタン非活性
                    stoneManager.setKomaInfo("WHITE");
                }
                else if (int.Parse(stageArray[index]) == 2)
                {
                    //黒い石 ボタン非活性
                    stoneManager.setKomaInfo("BLACK");
                }
            }
        }

        //ブロック画像を非表示
        blockImg.gameObject.SetActive(false);
    }

    //石のボタンを押下時処理
    public void pushKoma(GameObject obj)
    {
        //タップしたインデックスを取得
        int tapIndex = int.Parse(obj.name);

        //選択リストに追加(黒)
        selectArray.Add(tapIndex);

        //黒い石を配置
        stoneList[tapIndex].setKomaInfo("BLACK");

        //プッシュ回数を1プラス
        pushIndex += 1;

        //チェック処理
        checkKoma();
    }

    //チェック処理
    void checkKoma()
    {
        //絞り込んだ新しい配列を生成
        List<List<int>> nextArray = new List<List<int>>();

        //複数回答分回す   
        for (int i = 0; i < correctArray.Count; i++)
        {

            //リストを取得
            List<int> array = correctArray[i];

            //1手1手見ていく
            //1つでも違う手だったら除外
            bool isOk = true;
            for (int j = 0; j < pushIndex; j++)
            {
                if (selectArray[j] != array[j])
                {
                    isOk = false;
                }
            }
            //今の手とすべて同じ場合
            if (isOk)
            {
                //絞り込みのリストに追加
                nextArray.Add(array);
            }
        }

        //絞り込みの件数によって分岐
        if (nextArray.Count == 0)
        {
            //全部不正解
            wrongImg.enabled = false;
            blockImg.gameObject.SetActive(true);
        }
        else
        {
            //クリア判定
            //絞り込みが1件ですべてタップした場合
            if (nextArray.Count == 1 && nextArray[0].Count == pushIndex)
            {
                //クリア
                correctImg.enabled = false;
                blockImg.gameObject.SetActive(true);
            }
            else
            {
                //残った配列からランダムに選ぶ
                int random = Random.Range(0, nextArray.Count);
                List<int> next = nextArray[random];

                //選択配列に追加(白)
                selectArray.Add(next[pushIndex]);

                //白い石を設定
                stoneList[next[pushIndex]].setKomaInfo("WHITE");

                //プッシュ回数を1プラス
                pushIndex++;

                //絞り込み配列に入れ替える
                correctArray = nextArray;
            }
        }
    }

    //もう一度ボタン押下処理
    public void pushOnce()
    {
        //正解or不正解画像を非アクティブにする
        correctImg.enabled = false;
        wrongImg.enabled = false;
        //画面を読み込みなおす
        SceneManager.LoadScene("Problem");
    }
}
public class StoneManager : MonoBehaviour
{

    public Image blackStoneImg;
    public Image whiteStoneImg;
    public Button komaBtn;

    void Start ()
    {

    }

    //石の情報をセット
    public void setKomaInfo(string kbn)
    {
        if (kbn == "WHITE")
        {
            //白石の設定
            whiteStoneImg.gameObject.SetActive(true);
            komaBtn.gameObject.SetActive(false);
        }
        else if (kbn == "BLACK")
        {
            //黒石の設定
            blackStoneImg.gameObject.SetActive(true);
            komaBtn.gameObject.SetActive(false);
        }
        else if (kbn == "NONE")
        {
            //石なしの設定
            whiteStoneImg.gameObject.SetActive(false);
            blackStoneImg.gameObject.SetActive(false);
            komaBtn.gameObject.SetActive(true);
        }
    }
}

試したこと

・時間をあけて実行、日を変えて実行
・2重ループのループ回数を減らす

補足情報(言語/FW/ツール等のバージョンなど)

Unity歴:1ヶ月
開発ツール:Unity5.5.2f1 Personal(64bit)

【PCスペック】
OS:win7 64bit
CPU:Intel Corei7 3610QM 2.30GHz
RAM:8GB
グラフィックボード:Intel(R) HD Graphics 4000

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

+3

以下のコードが原因なんじゃないですか?

//親オブジェクトを設定
obj.transform.parent = stonePrefab.transform;

これだとループする度にプレハブの子要素にオブジェクトが追加されて、Instantiateでそれらの子要素も含めて生成されてメモリを食ってしまうのではないでしょうか?(くわしいことはよくわ)
実際簡易的なコードで試して見たら止まりましたw

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/03/31 19:04

    指摘の箇所のstonePrefabをpanelBasePosiに修正してみたら実行することができました!
    ありがとうございました!

    キャンセル

+2

doubutweet さんがおっしゃっているのが正解だと思います。

//石を生成
GameObject obj = (GameObject)Instantiate(stonePrefab);
//親オブジェクトを設定
obj.transform.parent = stonePrefab.transform;

親オブジェクトを設定と書いてありますが、つまり stonePrefab の子オブジェクトを増やすことと同義です。

1ループ目は

stonePrefab
└ stonePrefab (Clone 1)

となるでしょうが、2ループ目ではすでに stonePrefabの子要素も一緒に Instantiate されますので

GameObject obj = (GameObject)Instantiate(stonePrefab); の obj には1ループ目でセットされた子要素も含まれ以下のようになります。

stonePrefab
└ stonePrefab (Clone 2)
    └ stonePrefab (Clone 1) <= 1ループ目に生成された子供

for文でループするたびに一度にコピーするオブジェクトが増えていってしまいます。
stonePrefabの他に、親オブジェクトを用意してあげるのが良いと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/04/02 07:09

    分かりやすい説明ありがとうございます!
    かなり助かりました!
    1人で悩んでましたが、質問すれば一発でしたね(汗

    キャンセル

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

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

関連した質問

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