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

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

ただいまの
回答率

90.03%

Unity プレハブを作りオブジェクトそれぞれ処理を実行

解決済

回答 2

投稿

  • 評価
  • クリップ 1
  • VIEW 1,702

torano

score 81

環境

Unity5.4
mono4.4.2

やっていること

ゲームオブジェクトを適当にひとつ作りフォントを黄色にした後、以下のスクリプトをコンポーネントとして埋め込みました。

このスクリプトはマウスでクリックした場所にそのオブジェクトがあれば赤色にし、時間経過で元の黄色に戻るというスクリプトです。

using UnityEngine;
using System.Collections;

public class myscript : MonoBehaviour {
    GameObject obj;
    Color c;
    bool flg = false;
    int counter = 0;

    void Update () {
        if (Input.GetMouseButtonDown (0)) {
            Vector3 pos = Input.mousePosition;
            Ray ray = Camera.main.ScreenPointToRay (pos);
            RaycastHit hit;

            if (Physics.Raycast (ray, out hit, 100f)) {
                obj = hit.collider.gameObject;
                c = obj.GetComponent<Renderer> ().material.color;
                obj.GetComponent<Renderer> ().material.color = Color.red;
                counter = 100;
                flg = true;
            }
        }

        if (flg) {
            if (--counter == 0) {
                flg = false;
                obj.GetComponent<Renderer> ().material.color = c;
            }
        }

    }
}


実行し、クリックすると少しの間赤色になった後、黄色に戻ります。

このゲームオブジェクトのプレハブを作り、ゲームオブジェクトを幾つか複製して実行。

わからないこと

赤色にはなるのですが、少し待っても元の色(黄色)に戻りません。

いろいろ試行錯誤してみて、スクリプトを以下のように変更してみました。

using UnityEngine;
using System.Collections;

public class myscript : MonoBehaviour {
    GameObject obj;
    Color c;
    bool flg = false;
    int counter;

    void Start () {
        c = GetComponent<Renderer> ().material.color;    // 変更点
    }

    void Update () {
        if (Input.GetMouseButtonDown (0)) {
            Vector3 pos = Input.mousePosition;
            Ray ray = Camera.main.ScreenPointToRay (pos);
            RaycastHit hit;

            if (Physics.Raycast (ray, out hit, 100f)) {
                obj = hit.collider.gameObject;
                obj.GetComponent<Renderer> ().material.color = Color.red;
                counter = 100;
                flg = true;
            }
        }

        if (flg) {
            if (--counter == 0) {
                flg = false;
                GetComponent<Renderer> ().material.color = c;    // 変更点
            }
        }

    }
}


このスクリプトだと最後にすべて黄色に戻りますが、想定していた結果とは違いました。

これだと、いくつか赤色にした場合、すべて一斉に黄色になってしまします。それぞれのオブジェクトが、それぞれクリックした時間から一定時間後に元の色に戻る、というようにしたいです。

例えば、A,Bというオブジェクト(プレハブ)があったとします。まずAをクリックして赤色にし、次にBをオブジェクトを赤にします。Aを最初に押したので、Aが先に元の色に戻り、次にBが元の色に戻る、というようにです。

疑問点

一つ目は、最初のスクリプトではなぜ元の色に戻らないか。

二つ目は、どのようにすればうまくいくか。

よろしくお願いします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 2

checkベストアンサー

0

ソースを見たところ、クリック時の動作がクリックされたオブジェクトを赤色にする&自分のカウンターを100にする&フラグを立てるとなっています。  
つまり、あるオブジェクトの被クリックに対し、全てのオブジェクトが一斉に反応しています。  
1、2の両疑問点共にここが原因と思われます。

 疑問点1

何かしらのオブジェクトがクリックされた場合、全てのオブジェクトがクリックされたオブジェクトを赤色にしています。  
それぞれのオブジェクトはこのとき赤色にしたオブジェクトを覚えておき、カウンターが0となった際に記憶していたオブジェクトを黄色にしています。  
単体のオブジェクトでしたら問題ありませんが、複数のオブジェクトが同様のロジックで動いた場合、黄色にすべきオブジェクトの変数には最後に赤色になったオブジェクトが共通で入っていることになります。  
このため最後にクリックされたオブジェクト以外は時間経過で黄色になりません。  
流れとしては恐らく以下のようになっております。時間経過で下方向へ状態が変化します。

オブジェクトA.obj オブジェクトB.obj 操作
Null Null
オブジェクトA オブジェクトA オブジェクトAをクリック
オブジェクトB オブジェクトB オブジェクトBをクリック
100フレーム経過
オブジェクトB オブジェクトB

両オブジェクト共にオブジェクトBを黄色くするため、オブジェクトAは赤いままとなります。  
直接的な解決策としては、二つ目に挙げられたソースのロジックで問題無いと思われます。

 疑問点2

上で述べた通り、何かしらのオブジェクトクリック時には全てのオブジェクトが反応しています。  
このとき全てのオブジェクトが一斉にタイマーの初期処理も行っているため、結果としてタイマーの始点が揃っていると思われます。  
例示されていたA,Bオブジェクトの挙動を図で示しますと、恐らく以下のようになっております。

オブジェクトA オブジェクトB 操作
Aの被クリックに反応 Aの被クリックに反応 オブジェクトAをクリック
Aを赤色にする Aを赤色にする
Aのカウンター初期化 Bのカウンター初期化
10フレーム経過
Bの被クリックに反応 Bの被クリックに反応 オブジェクトBをクリック
Bを赤色にする Bを赤色にする
Aのカウンター初期化 Bのカウンター初期化
100フレーム経過
Aを黄色にする Bを黄色にする A,B両方のカウンターが0以下になる

解決策としては

  •  クリック時の条件にクリックされたオブジェクトが自分自身であるという条件を加える
  •  既にフラグが立っていればタイマーの初期化処理をしない  

等が考えられます。  

なお個人的な意見ですが、この手のアクションをコードベースで制御するのであればコルーチンを用いた方が簡易ではないかと思います。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2016/08/20 20:42 編集

    ありがとうございます。なぜうまくいかないかがよくわかりました。要するにクリックするたびに2つ目のif文内の処理が行われていて、objが最後にクリックしたオブジェクトになっているわけですね。

    2つ目のスクリプトを以下のように変更してみたらうまくいきました。

    2つ目のif文の条件をPhysics.Raycast (ray, out hit, 100f) && hit.collider.gameObject.name == gameObject.name)に。
    最後の色を元に戻す部分をobj.GetComponent<Renderer> ().material.color = c;に。

    他の解決策も試してみたのですが実装の仕方がよくわかりませんでした。。。
    もしよろしければ教えてください。よろしくお願いします。

    キャンセル

  • 2016/08/24 06:02

    hit.collider.gameObject.name == gameObject.nameですと、偶然別オブジェクト同士の名称が同じ場合に判定がおかしくなります。hit.collider.gameObject == gameObjectの方が正確な判定となります。

    なお「既にフラグが立っていればタイマーの初期化処理をしない」方法ですが、よくよく考えてみたところ結局上記の判定が別途必要になりました。
    フラグの管理のみでこの問題を解決するのは困難かと思われます。申し訳ありません。

    コルーチンとは、フレーム毎の連側的な状態変化を関数内での繰り返し処理として記述する手法です。
    「一連のアクション」を実装する場合、非常に有用となります。
    コルーチンに関しましては、既に多数の解説が存在しています。「コルーチン」の名称で色々と調べてみてください。

    キャンセル

  • 2016/08/25 21:00

    ありがとうございます!

    キャンセル

0

using UnityEngine;
using System.Collections;

public class myscript : MonoBehaviour {
    GameObject obj;
    Color c;
    bool flg;
    int counter;

    void Start () {
        c = GetComponent<Renderer> ().material.color;
    }

    void Update () {
        if (Input.GetMouseButtonDown (0)) {
            Vector3 pos = Input.mousePosition;
            Ray ray = Camera.main.ScreenPointToRay (pos);
            RaycastHit hit;

            if (Physics.Raycast (ray, out hit, 100f) && hit.collider.gameObject.name == gameObject.name) {
                obj = hit.collider.gameObject;
                obj.GetComponent<Renderer> ().material.color = Color.red;

                flg = true;
                counter = 100;
            }
        }

        if (flg) {
            Debug.Log (counter);

            if (--counter == 0) {
                flg = false;
                obj.GetComponent<Renderer> ().material.color = c;
            }
        }

    }
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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