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

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

ただいまの
回答率

88.64%

Unityで、一番近くの敵に自動で弾を撃つタレットをつくりたいのですが…

解決済

回答 3

投稿

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

thaya

score 4

現在、「指定した1つのオブジェクトに向かって弾を撃つ」スクリプトと「指定したタグを持つオブジェクトの中で一番近いものを見つける」スクリプトを拾ったので、どうにかくっつけて「指定したタグをもつオブジェクトの中で一番近いものに向かって弾を撃つ」スクリプトにしたいのですが、うまく組み合わせることができません。

以下の二つを組み合わせて使う方法、あるいは他の方法でも今回やりたいことが実現できる方法があれば教えてください。

【指定した1つのオブジェクトに向かって弾を撃つ】

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

public class LookAt_Pod : MonoBehaviour
{
    public GameObject BulletPrefab;
    public float bulletSpeed;
    public float span = 1f;
    private int timeCount = 0;
    private GameObject targetObj; // 注視したいオブジェクト

    void Update()
    {
        this.transform.LookAt(targetObj.transform);

        timeCount += 1;

        if (timeCount > span)
        {
            timeCount = 0;
            // 敵の弾を生成する
            GameObject Bullet = Instantiate(BulletPrefab, transform.position, Quaternion.identity);

            Rigidbody BulletRb = Bullet.GetComponent<Rigidbody>();

            // 弾をforwardに飛ばす
            BulletRb.AddForce(transform.forward * bulletSpeed);

            // 3秒後に弾を削除する。
            Destroy(Bullet, 3.0f);
        }
    }
}

【指定したタグを持つオブジェクトの中で一番近いものを見つける】

    GameObject SerchEnemy(string Enemy)
    {
        float dis = 0;           //距離保存用
        float nearDis = 0;          //最も近いオブジェクトの距離        
        GameObject targetObj = null; //オブジェクト

        //  指定したタグのオブジェクトをすべて取得する
        foreach (GameObject obj in GameObject.FindGameObjectsWithTag(Enemy))
        {
            //  敵との距離を計算
            dis = Vector3.Distance(obj.transform.position, transform.position);

            //  より近いオブジェクトか、距離が0のオブジェクトなら更新
            if (nearDis > dis || dis == 0)
            {
                nearDis = dis;          //  距離を保存            
                targetObj = obj;        //  ターゲットを更新
            }
        }
        //最も近かったオブジェクトを返す
        return targetObj;
    }


よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 3

+2

LookAt_Pod側にSetTargetObj関数を実装し、
SerchEnemyの返り値を渡す感じじゃ無理なのでしょうか?

LookAt_Pod pod;
EnemyManager enemyManager;

pod.SetTargetObj(enemyManager.SerchEnemy());

また、単純にLookAt_PodクラスにSerchEnemy関数を実装しちゃえば
こんな感じでできそう

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

public class LookAt_Pod : MonoBehaviour
{
    public GameObject BulletPrefab;
    public float bulletSpeed;
    public float span = 1f;
    private int timeCount = 0;
    private GameObject targetObj; // 注視したいオブジェクト

    void Update()
    {
        this.transform.LookAt(SerchEnemy().transform);

        timeCount += 1;

        if (timeCount > span)
        {
            timeCount = 0;
            // 敵の弾を生成する
            GameObject Bullet = Instantiate(BulletPrefab, transform.position, Quaternion.identity);

            Rigidbody BulletRb = Bullet.GetComponent<Rigidbody>();

            // 弾をforwardに飛ばす
            BulletRb.AddForce(transform.forward * bulletSpeed);

            // 3秒後に弾を削除する。
            Destroy(Bullet, 3.0f);
        }
    }

   GameObject SerchEnemy(string enemy)
   { 
      // 省略
   }

}

でもこれ、懸念点があるとすれば、
LookAt_Podクラスが複数あると、
複数のLookAt_Podが全員、敵を全検索してしまうので、
やはりSerchEnemy関数はEnemyManagerのようなもので持っておいて、
LookAt_Podにセットする形がふさわしいかなと思います!

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/02/21 11:23

    > LookAt_Pod.SerchEnemy(string)' の必要な仮パラメーター 'Enemy' に対応する特定の引数がありません。
    こちらはあなたの作った通りstring型の引数を渡してあげてください。

    今回、この二つのスクリプトはきちんと動いている、という認識でしたが、
    それは合っていますでしょうか?

    あと、タグの処理を含んでいるので、敵のオブジェクトにきちんとEnemyタグがついているかどうかも確認お願いいたします!

    キャンセル

  • 2020/02/22 02:52

    すみません、スクリプトがちゃんと動くかどうかもテストしていませんでした。

    【指定した1つのオブジェクトに向かって弾を撃つ】のほうは動作しますが、【指定したタグを持つオブジェクトの中で一番近いものを見つける】のほうは拾い物ののスクリプトをそのままコピペしただけです。
    http://hellomoneyworld.seesaa.net/article/449510462.html
    ブログに書くくらいだから、エラーもでてないし当然動作しているんだろうと、安易に思い込んでしまっていました。

    Dbag.Logをうまく組み込んだりして、動作の確認をすることすら今の自分にはできないので、とりあえず質問してみれば答えがもらえるのかなと…考えが甘かったです…

    タグは一応ちゃんとついてます。

    キャンセル

  • 2020/02/25 11:17

    一旦頑張って各機能が動くかどうかを試してみたから合成させたほうがいいですね
    これは単体テストっていうもので、
    一つの機能ごとに動くことを確認しないと、
    何が間違っているのかがわからないので手の出しようがなくなってしまうからです…!

    キャンセル

+1

public class LookAt_Pod : MonoBehaviour
{
    // (中略)

    GameObject SerchEnemy(string Enemy)
    {
        // 中身は2つ目のコードと同じ
    }

    void Update()
    {
        targetObj = SerchEnemy("タグ名");

        // 続きは1つ目のコードと同じ
    }
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/02/19 22:43

    はい、そうです。
    あいまいな書き方をしてしまって申し訳ありません。

    キャンセル

  • 2020/02/19 22:46

    そうであれば、見つからない場合はtargetObjはnullなので、その場合は(「this.transform.LookAt(targetObj.transform);」などの)処理をしない、ということをする必要があります。
    (先ほど言った「場合分け」とはこういう意味です)

    キャンセル

  • 2020/02/19 23:07

    正直ちゃんと理解できてはいませんが、自分がやりたかった「指定したタグをもつオブジェクトの中で一番近いものに向かって弾を撃つ」という動きが単純にふたつのスクリプトを組み合わせるだけでは、動作しないということはわかりました。

    ちゃんと動かすためには、他にもいろいろと処理が必要なんですね…
    何度も返信してくださって、ありがとうございます。

    キャンセル

check解決した方法

0

https://blog.beatdjam.com/entry/2014/10/22/032751
このサイトに載っていたスクリプトを使用することで、やりたかった動きを実現することができたので、自己解決ということにさせていただきたいと思います。

回答やアドバイスをくださった方々、ありがとうございました。
とても参考になりました。

今回つくったタレットは、範囲の指定がなくシーン内の対象タグを持つオブジェクトをすべて狙ってしまうので、実際にゲームで使用するためには、もう一工夫必要だと思いますが、自分と同じようなものをつくりたいと思った初心者の方のためにコードを置いておこうと思います。

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

public class LookAt_Enemy : MonoBehaviour
{
    public GameObject BulletPrefab;
    public float bulletSpeed;
    public float span = 1f;
    private int timeCount = 0;

    private GameObject nearObj;         //最も近いオブジェクト
    private float searchTime = 0;    //経過時間

    // Use this for initialization
    void Start()
    {
        //最も近かったオブジェクトを取得
        nearObj = serchTag(gameObject, "Enemy");
    }

    // Update is called once per frame
    void Update()
    {

        //経過時間を取得
        searchTime += Time.deltaTime;

        if (searchTime >= 0.1f)
        {
            //最も近かったオブジェクトを取得
            nearObj = serchTag(gameObject, "Enemy");

            //経過時間を初期化
            searchTime = 0;
        }

        if (nearObj == null)
        {
            this.transform.rotation = Quaternion.identity;   //敵がいない場合は回転をリセット
        }
        else
        {
            //対象の位置の方向を向く
            transform.LookAt(nearObj.transform);

            timeCount += 1;

            if (timeCount > span)
            {
                timeCount = 0;
                // 敵の弾を生成する
                GameObject Bullet = Instantiate(BulletPrefab, transform.position, Quaternion.identity);

                Rigidbody BulletRb = Bullet.GetComponent<Rigidbody>();

                // 弾をforwardに飛ばす
                BulletRb.AddForce(transform.forward * bulletSpeed);

                // 3秒後に弾を削除する。
                Destroy(Bullet, 3.0f);
            }
        }
    }

    //指定されたタグの中で最も近いものを取得
    GameObject serchTag(GameObject nowObj, string tagName)
    {
        float tmpDis = 0;           //距離用一時変数
        float nearDis = 0;          //最も近いオブジェクトの距離
        GameObject targetObj = null; //オブジェクト

        //タグ指定されたオブジェクトを配列で取得する
        foreach (GameObject obs in GameObject.FindGameObjectsWithTag(tagName))
        {
            //自身と取得したオブジェクトの距離を取得
            tmpDis = Vector3.Distance(obs.transform.position, nowObj.transform.position);

            //オブジェクトの距離が近いか、距離0であればオブジェクト名を取得
            //一時変数に距離を格納
            if (nearDis == 0 || nearDis > tmpDis)
            {
                nearDis = tmpDis;
                targetObj = obs;
            }

        }
        //最も近かったオブジェクトを返す
        return targetObj;
    }
}

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

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

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

関連した質問

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