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

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

新規登録して質問してみよう
ただいま回答率
85.49%
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

Q&A

解決済

2回答

4966閲覧

【Unity】敵を倒してくれる仲間のNPCを作りたい

jum6948

総合スコア20

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

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

0グッド

4クリップ

投稿2018/01/17 08:06

編集2018/01/19 04:13

###前提・実現したいこと
Unity3Dを使ってTPS視点のアクションゲームを作っています。
プレイヤーと一緒に戦うノンプレイヤーキャラを作っているのですが、一匹目のモンスターを倒したあと
次の行動に移行するまでに時間がかかります。

###発生している問題・エラーメッセージ

2匹めの敵も攻撃するようにりましたが、Update内がうまく言っておりません。
今は、原因究明中です。

###該当のソースコード(2018/01/19改定)

C#

1 [Header("基本設定")] 2 Rigidbody rb; 3 AudioSource aud; 4 5 [Header("対象オブジェクト")] 6 GameObject targetP; 7 public float PlayerLength; //プレイヤーとの設定距離 8 public float PlayerDistance; //プレイヤーとの今の距離 9 10 public Transform target; 11 12 public Transform[] enemyList; //敵のリスト 13 public Transform targetE; //現在選択されている敵 14 public float EnemyDistance; //一番近い敵との距離 15 16 [Header("行動しているか?")] 17 public bool nowExecCoroutine; 18 19 [Header("再攻撃と再移動")] 20 //再攻撃までの時間 21 public float atackInterval = 0; 22 public float atackIntervalMax = 2.0F; 23 24 //再移動までの時間 25 public int moveInterval = 0; 26 public int TukiBunki; 27 28 //アニメーション関係 29 public Animator animator; 30 31 [Header("スクリプト参照")] 32 private EnemyState EState; //EnemyStateスクリプトへの参照 33 private PlayerGuardChecker PGCheck; //PlayerGuardCheckerスクリプトへの参照 34 private NonPcState NPState; 35 36 37 [Header("巡回")] 38 private NavMeshAgent agent; 39 40 // Use this for initialization 41 void Start () { 42 //ターゲットを取得 43 targetP = GameObject.FindGameObjectWithTag("Player"); 44 45 //targetE = GameObject.FindGameObjectWithTag("Enemy"); 46 atackInterval = 0; 47 moveInterval = 200; 48 49 //スクリプト参照 50 EState = GetComponent<EnemyState>(); 51 PGCheck = GetComponent<PlayerGuardChecker>(); 52 53 //音とアニメーション 54 animator = GetComponent<Animator>(); 55 aud = gameObject.GetComponent<AudioSource>(); 56 rb = GetComponent<Rigidbody>(); 57 58 //初期モーション 59 animator.SetBool("Idle", true); 60 61 //NPCの位置チェック 62 //StartCoroutine(NpcPositionCheck()); 63 } 64 65 // Update is called once per frame 66 void Update() 67 { 68 enemyList = GameObject.FindGameObjectsWithTag("Enemy").Select(g => g.transform).OrderBy(g => (g.position - target.position).sqrMagnitude).ToArray(); 69 70 //なんらかの行動をしているか? 71 if (!nowExecCoroutine) 72 { 73 //プレイヤーが近くにいるか? 74 //いる 75 if (PlayerDistance >= 100) 76 { 77 //敵がいない 78 if (enemyList.Length == 0) 79 { 80 if (PlayerDistance > 50) 81 { 82 nowExecCoroutine = true; 83 StartCoroutine(NpcNormalWalk()); 84 Debug.Log("敵がいないのでついていく"); 85 86 } 87 else //if(Vector3.Distance(targetP.transform.position, transform.position) >= 7) 88 { 89 //すごく近いので停止 90 nowExecCoroutine = true; 91 StartCoroutine(NpcNormalKaiten()); 92 Debug.Log("敵がいないので安心の停止"); 93 } 94 } 95 //敵がいる 96 else if (enemyList.Length > 0) 97 { 98 SelectEnemy(0); 99 Debug.Log("tagE" + targetE + "|" + "enemyList0" + enemyList[0]); 100 101 //敵が近くにいるか? 102 //いる 103 if (EnemyDistance >= 80)//80 104 { 105 //敵がいるので抜刀 106 StartCoroutine(NpcKamaeIdle()); 107 Debug.Log("構える"); 108 } 109 //殴れない、近づく 110 else if (EnemyDistance >= 50 && EnemyDistance > 5)//50 111 { 112 nowExecCoroutine = true; 113 StartCoroutine(NpcKamaeWalk()); 114 Debug.Log("敵に近づく"); 115 } 116 else if (EnemyDistance <= 5)//20 117 { 118 nowExecCoroutine = true; 119 StartCoroutine(NpcKamaeTuki()); 120 Debug.Log("敵につき"); 121 } 122 } 123 } 124 //いないが30メートル以内にいるか?) 125 else if (PlayerDistance <= 110) 126 { 127 nowExecCoroutine = true; 128 StartCoroutine(NpcNormalKaiten()); 129 Debug.Log("遠くから見る"); 130 } 131 //いない 132 else 133 { 134 nowExecCoroutine = true; 135 StartCoroutine(NpcNormalIdle()); 136 Debug.Log("待機"); 137 } 138 139 140 141 } 142 } 143 //LateUpdateは、Updateが実行されたあと一回だけ実行される 144 void LateUpdate() 145 { 146 //Find all enemies within range and add them to list. Order list from closest to furtherest 147 //範囲内のすべての敵を見つけてリストに追加します。furtherestに最も近いものからの注文リスト 148 enemyList = GameObject.FindGameObjectsWithTag("Enemy").Select(g => g.transform).OrderBy(g => (g.position - target.position).sqrMagnitude).ToArray(); 149 150 //一番近い敵との距離を算出 151 if (targetE != null) 152 { 153 EnemyDistance = Vector3.Distance(targetE.transform.position, transform.position); 154 } 155 //プレイヤーとの距離を算出 156 PlayerDistance = Vector3.Distance(targetP.transform.position, transform.position); 157 } 158 159 void SelectEnemy(int i) 160 { // 0 = Nearest / New List, 1 = Forwards, -1 = Backwards 161 switch (i) 162 { 163 case 0: 164 targetE = enemyList.FirstOrDefault(); 165 break; 166 default: 167 targetE = enemyList[((System.Array.FindIndex(enemyList, g => g == targetE) + i) % enemyList.Length + enemyList.Length) % enemyList.Length]; 168 break; 169 } 170 Debug.Log("敵がいる" + targetE); 171 } 172 173 public void NpcDead() 174 { 175 StartCoroutine(NpcResurrection()); 176 } 177 178 void moveResaet() 179 { 180 animator.SetBool("Idle", false); 181 animator.SetBool("Walk", false); 182 animator.SetBool("Kamae", false); 183 animator.SetBool("Dead", false); 184 } 185

###試したこと

###補足情報(言語/FW/ツール等のバージョンなど)
【環境】
Unity5.6.1f1 personal
win10
MicrosoftVisualStudio
上記が作成環境になっております。

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

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

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

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

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

guest

回答2

0

ベストアンサー

敵をDestroyしたのに、UpDate内ではその破棄されたゲームオブジェクトに継続アクセスし続けているが参照できないというエラーのようです。

targetE = GameObject.FindGameObjectWithTag("Enemy");

Vector3.Distance(targetE.transform.position, transform.position)で参照し続けているからではないでしょうか。

敵がnullであったなら計算しないという処理を追加してみるのはどうでしょうか。

ゲームオブジェクトの全滅判定ならGameObjectの配列に敵を格納してGameObject.FindGameObjectsWithTag.Lengthで数えるといいと思います。

FindGameObjectは重い処理らしいのでUpDate内で毎フレームでの取得はやめておいた方が良いです。
コルーチンで定期的にとか、何かのアクションが終わるタイミングとかで使うといいと思います。

コルーチンでシーン内にいる敵の数を1秒毎にカウントしてコンソールに表示するサンプルスクリプトを考えてみました。

C#

1public class Ecount : MonoBehaviour { 2 3 GameObject[] targetE; 4 5 void Start () { 6 StartCoroutine("EnemyCount"); 7 } 8 9 IEnumerator EnemyCount() { 10 11 for (;;) { 12 targetE = GameObject.FindGameObjectsWithTag("Enemy"); 13 Debug.Log(targetE.Length); 14 15 yield return new WaitForSeconds(1.0f); 16 } 17 } 18}

これを応用すれば全滅判定は出来ると思います。

追加サンプルコード

C#

1using UnityEngine; 2 3public class ExistEnemyCount : MonoBehaviour { 4 5 GameObject[] targetE; 6 7 void Start() { 8 9 ExistEnemy(); 10 } 11 12 void Update() { 13 14 if (Input.GetKeyDown(KeyCode.G)) { 15 16 ExistEnemy(); 17 18 if (targetE.Length == 0) { 19 Debug.Log("敵はいない"); 20 }else 21 if (targetE.Length >= 1) { 22 Debug.Log("敵はまだ"+ targetE.Length+"体存在する"); 23 } 24 } 25 } 26 27 //シーン内の敵の数をサーチする 28 void ExistEnemy() { 29 targetE = GameObject.FindGameObjectsWithTag("Enemy"); 30 } 31}

投稿2018/01/17 09:46

編集2018/01/20 01:28
cloudmaterial

総合スコア163

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

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

jum6948

2018/01/19 04:22 編集

すいません、技術不足で応用できませんでした。 質問なのですが、【for(;;)】というのはどういう条件なのでしょうか? 【現状報告】 敵が2体いる時に、1体目を倒したあと2体目を狙ってくれなかったのですが ネットで拾ったLinQというものを使うのを試しました。 ``` //Find all enemies within range and add them to list. Order list from closest to furtherest //範囲内のすべての敵を見つけてリストに追加します。furtherestに最も近いものからの注文リスト enemyList = GameObject.FindGameObjectsWithTag("Enemy").Select(g => g.transform).OrderBy(g => (g.position - target.position).sqrMagnitude).ToArray(); ``` というものを足して、2体目を狙うようになってくれました。 またtargetEをgameobjectからtransformに変更しました。
cloudmaterial

2018/01/19 13:19 編集

for(;;)は単にEnemyCount()を随時機能させるための無限ループです。簡単にコルーチンで使うために使用しました、それを使うべきということではありません。ようするに、サンプルコードEcountは、targetE.Lengthで敵の数が取得できるということを言いたかっただけです。 GameObject.FindGameObjectsWithTagは重い処理なので、UpDate内で毎フレームでは使わない方が良いです。サンプルをコルーチンにしなかった方が良かったかもしれません。回答の方へ違うサンプルコードを追加しました。 サンプルコード「ExistEnemyCount」はGキーを押すとExistEnemy()を実行しシーン内のEnemyタブの付いたゲームオブジェクトを配列に格納します。そして、シーン内にいるEnemyの数によって条件分岐でメッセージが変わります。このような感じで、敵を倒してDestroyするタイミングなどでメソッドを呼び出してその都度サーチするようにすればUpdate内でも負荷をかけず条件分岐に使えるわけです。 シーン内にいる敵の中で最も近い敵の取得方法はLinQまで使わずとも実装は出来るとは思いますが、LinQを使う方法は当方の回答できる範囲を超えているので何とも言えません。「Unity5ゲーム開発レピシ」(翔泳社)という書籍の中の「一番近い敵をロックオンするには」という項目にLinQを使わずにシンプルに一番近い敵を取得する方法の解説が載っています。一度読んでみると参考になるかもしれません。 当方としては、今回の質問内容はエラーの原因とシーン内の敵の数の取得方法を知りたいということだと判断してそれについて回答しました。このように質問内容が変わっていくものだとは想定していませんでした。このような大規模なプロジェクトとなると付きっきりで完成までのお付き合いはちょっと出来ません。また、このように回答者が何に対して回答したかの質問を消していくのはどうかとも思います。問題が一つ解決したのなら、また新たに次の質問スレッドを立てていく方が質問の内容が残りますし、回答してくれる方も見つけやすいし回答しやすいと思いますが、どうでしょう。申し訳ありませんが、私の回答はここまでにさせて下さい。回答できる方がいらっしゃいましたら、どなたか引き継いでもらえたらと思います。
jum6948

2018/01/19 13:19

修正した、文言をいれると、文字数オーバーで削らなければならなかったので、最初の内容は、解決したので、消してしまいました。 認識が甘かったです。申し訳ありませんでした。
guest

0

Linqを使って解決しました。
最善の正解かどうかわかりませんが、「敵を倒す、次の敵を狙う」の行動はできるようになっていると思います。

c#

1using System.Collections; 2using System.Collections.Generic; 3using System.Linq; 4using UnityEngine; 5using UnityEngine.AI; 6 7 GameObject targetP; //プレイヤー 8 public Transform target; //プレイヤーの位置 9 public Transform[] enemyList; //敵のリスト 10 public Transform targetE; //現在選択されている敵 11 12void Start() 13{ 14 //ターゲットを取得 15 targetP = GameObject.FindGameObjectWithTag("Player"); 16 target = targetP.transform; 17} 18void update 19{ 20 //敵がいない 21 if (enemyList.Length == 0) 22 { 23 //敵がいない時に実行する内容 24 } 25 //敵がいる 26 else if (enemyList.Length > 0) 27 { 28 //対象を選択する 29 SelectEnemy(0); 30 //敵がいる時に実行する内容 31 } 32} 33//LateUpdateは、Updateが実行されたあと一回だけ実行される 34void LateUpdate() 35{ 36 //Find all enemies within range and add them to list. Order list from closest to furtherest 37 //範囲内のすべての敵を見つけてリストに追加します。furtherestに最も近いものからの注文リスト 38 enemyList = GameObject.FindGameObjectsWithTag("Enemy").Select(g => g.transform).OrderBy(g => (g.position - target.position).sqrMagnitude).ToArray(); 39 40 //一番近い敵との距離を算出 41 if (targetE != null) 42 { 43 EnemyDistance = Vector3.Distance(targetE.transform.position, transform.position); 44 } 45 //プレイヤーとの距離を算出 46 PlayerDistance = Vector3.Distance(targetP.transform.position, transform.position); 47} 48//敵を選択(0になる対象を選択) 49void SelectEnemy(int i) 50{ // 0 = Nearest / New List, 1 = Forwards, -1 = Backwards 51 switch (i) 52 { 53 case 0: 54 //最初に選ばれた敵をデフォルトにする 55 targetE = enemyList.FirstOrDefault(); 56 break; 57 default: 58 //デフォルトの条件:敵のリストの中から、 59 targetE = enemyList[((System.Array.FindIndex(enemyList, g => g == targetE) + i) % enemyList.Length + enemyList.Length) % enemyList.Length]; 60 break; 61 } 62}

今回は、思っていた回答を得られなかったので、自分の文章がおかしかったのかと思い、質問の仕方を変更しました。不快に思われた方、申し訳ありませんでした。

投稿2018/01/29 05:34

jum6948

総合スコア20

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問