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

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

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

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

Q&A

解決済

1回答

4388閲覧

一定の領域をランダムに動くNPCを作りたい

momonoki

総合スコア21

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

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

0グッド

0クリップ

投稿2021/06/26 05:27

編集2021/06/27 01:35

前提・実現したいこと

現在、Unityで2Dゲームを作成しており、一定の範囲を上下左右ランダムに進行するNPCを作成したいと考えています。具体的には、上下左右いずれかに一定時間移動⇨向きを変えて一定時間移動⇨向きを変えて一定時間移動⇨……といった動作です。また、移動中にNPCが範囲を超える場合は、方向を変えて(反対方向を向いて)移動させたいです。
現在、以下の質問・回答を参考に、概ね実装することができている状況です。
https://teratail.com/questions/285084

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

動かし続けていると以下のような挙動が発生します
①範囲の一番右側から一番左側にワープする(左から右もあり)
②範囲の一番右側に当たると、範囲の上下を高速で行き来する

該当のソースコード

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.UI; 5 6public class crowdHuman : MonoBehaviour 7{ 8 private float chargeTime;//動く時間 9 private float timeCount; 10 private float speed;//移動スピード 11 private float speed_Min;//Minスピード 12 private float speed_Max;//Maxスピード 13 Vector3 direction;//移動方向 14 Vector3 pos;//自分の位置 15 Vector2 minMoveArea = new Vector2(-8.9f, -3.9f);//移動範囲(最小) 16 Vector2 maxMoveArea = new Vector2(-5.9f, 2.9f);//移動範囲(最大) 17 18 // Start is called before the first frame update 19 void Start() 20 { 21 chargeTime = 1.0f; 22 23 //開始時の移動方向を決める 24 direction = RandomDirection(); 25 26 //移動スピードをランダムに決める 27 speed_Min = 0.005f; 28 speed_Max = 0.01f; 29 speed = Random.Range(speed_Min, speed_Max); 30 31 //自分の位置を取得 32 Transform myTransform = this.transform; 33 pos = myTransform.position; 34 } 35 36 // Update is called once per frame 37 void Update() 38 { 39 timeCount += Time.deltaTime; 40 41 //枠から出たら逆方向を向く 42 if ((pos.x < minMoveArea.x)||(pos.y < minMoveArea.y)||(pos.x > maxMoveArea.x)||(pos.y > maxMoveArea.y)) 43 { 44 Turn(); 45 //それ以外の場合は進む 46 } else 47 { 48 transform.position += direction * speed; 49 50 //指定した時間を経過すると 51 if (timeCount > chargeTime) 52 { 53 //進路をランダムに変更する 54 direction = RandomDirection(); 55 //TimeCountを0に戻す 56 timeCount = 0; 57 } 58 59 } 60 } 61 62 void Turn() 63 { 64 //タイムカウントを0に戻す。 65 timeCount = 0; 66 if (direction == Vector3.up) 67 { 68 transform.position = new Vector3(transform.position.x, maxMoveArea.y, transform.position.z); 69 direction = Vector3.down; 70 } 71 else if (direction == Vector3.down) 72 { 73 transform.position = new Vector3(transform.position.x, minMoveArea.y, transform.position.z); 74 direction = Vector3.up; 75 } 76 else if (direction == Vector3.left) 77 { 78 transform.position = new Vector3(minMoveArea.x, transform.position.y, transform.position.z); 79 direction = Vector3.right; 80 } 81 else if (direction == Vector3.right) 82 { 83 transform.position = new Vector3(maxMoveArea.x, transform.position.y, transform.position.z); 84 direction = Vector3.left; 85 } 86 } 87 88 Vector3 RandomDirection() 89 { 90 int r = Random.Range(0, 4); 91 switch (r) 92 { 93 default: 94 case 0: 95 return Vector3.up; 96 case 1: 97 return Vector3.down; 98 case 2: 99 return Vector3.left; 100 case 3: 101 return Vector3.right; 102 } 103 104 } 105} 106

追記コード

using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; public class crowdHuman : MonoBehaviour { private float chargeTime;//動く時間 private float timeCount; private float speed;//移動スピード private float speed_Min;//Minスピード private float speed_Max;//Maxスピード Vector3 direction;//移動方向 Vector3 pos;//自分の位置 Vector3 minMoveArea = new Vector3(-8.9f, -4.9f, 0);//移動範囲(最小) Vector3 maxMoveArea = new Vector3(-4.9f, 4.9f, 0);//移動範囲(最大) Vector3 WorldPoint; public Sprite[] HumanSprites; Sprite HumanSprite; // Start is called before the first frame update void Start() { chargeTime = 0.9f; //開始時の移動方向を決める direction = RandomDirection(); //移動スピードをランダムに決める speed_Min = 0.01f; speed_Max = 0.02f; speed = Random.Range(speed_Min, speed_Max); //自分の位置を取得 Transform myTransform = this.transform; pos = myTransform.position; HumanSprites = Resources.LoadAll<Sprite>("image"); int i = Random.Range(0, 3); HumanSprite = HumanSprites[i]; this.GetComponent<Image>().sprite = HumanSprite; } // Update is called once per frame void Update() { timeCount += Time.deltaTime; WorldPoint = transform.TransformPoint(pos); //枠から出たら逆方向を向く //if ((pos.x < minMoveArea.x)||(pos.y < minMoveArea.y)||(pos.x > maxMoveArea.x)||(pos.y > maxMoveArea.y)) if ((WorldPoint.x < minMoveArea.x) || (WorldPoint.y < minMoveArea.y) || (WorldPoint.x > maxMoveArea.x) || (WorldPoint.y > maxMoveArea.y)) { Turn(); //それ以外の場合は進む } else { transform.position += direction * speed; //指定した時間を経過すると if (timeCount > chargeTime) { //進路をランダムに変更する direction = RandomDirection(); //TimeCountを0に戻す timeCount = 0; } } } void Turn() { //タイムカウントを0に戻す。 timeCount = 0; if (WorldPoint.y > maxMoveArea.y) { transform.position = new Vector3(transform.position.x, maxMoveArea.y-0.2f, transform.position.z); direction = Vector3.down; } else if (WorldPoint.y < minMoveArea.y) { transform.position = new Vector3(transform.position.x, minMoveArea.y+0.2f, transform.position.z); direction = Vector3.up; } else if (WorldPoint.x < minMoveArea.x) { transform.position = new Vector3(minMoveArea.x+0.2f, transform.position.y, transform.position.z); direction = Vector3.right; } else if (WorldPoint.x > maxMoveArea.x) { transform.position = new Vector3(maxMoveArea.x, transform.position.y, transform.position.z); direction = Vector3.left; } } Vector3 RandomDirection() { int r = Random.Range(0, 4); switch (r) { default: case 0: return Vector3.up; case 1: return Vector3.down; case 2: return Vector3.left; case 3: return Vector3.right; } } }

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

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

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

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

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

guest

回答1

0

ベストアンサー

自分の環境でやってみましたが、このコードだと方向転換云々以前に範囲を突き抜けていきました。
「Vector3 pos」にStart()のときしか値が格納されておらず、以後ずっとその値を参照するのでコード側からしたらNPCがどう動いて今どの座標にいるかに関係なく、ずっと範囲内だと認識します。

Update()の「else」内を

C#

1transform.position += direction * speed; 2 3// 追記 4pos = transform.position; 5 6//指定した時間を経過すると 7if (timeCount > chargeTime) 8{ 9 //進路をランダムに変更する 10 direction = RandomDirection(); 11 //TimeCountを0に戻す 12 timeCount = 0; 13}

としてください。以下はこの状態でやったものとみなします。

①範囲の一番右側から一番左側にワープする(左から右もあり)

上記と同じように、質問のコードをコピペして実行すると、連続で瞬間移動する現象が確認できました。
Debug.Logで吐かせてみた結果、右側(左側)をはみだした場合、Turn()が実行されてもなお右側にはみだしている旨のLogが出ました。
これはposにTurn()で移動した座標が反映されておらず、はみだしたままのpositionを見てUpdate()のif文が判断しているためです。
また、瞬間移動が1度だけ起きる場合は、はみだした瞬間にUpdate()内で向きが変更され、それをTurn()が読み取って処理を行っている為起きると思われます。
解消方法としては、

C#

1// Update is called once per frame 2void Update() 3{ 4 timeCount += Time.deltaTime; 5 6 //枠から出たら逆方向を向く 7 if (pos.x < minMoveArea.x) 8 { 9 Turn(Vector3.left); 10 } 11 else if (pos.y < minMoveArea.y) 12 { 13 Turn(Vector3.down); 14 } 15 else if (pos.x > maxMoveArea.x) 16 { 17 Turn(Vector3.right); 18 } 19 else if (pos.y > maxMoveArea.y) 20 { 21 Turn(Vector3.up); 22 } 23 else // それ以外の場合は進む 24 { 25 transform.position += direction * speed; 26 27 //追記 28 pos = transform.position; 29 30 //指定した時間を経過すると 31 if (timeCount > chargeTime) 32 { 33 //進路をランダムに変更する 34 direction = RandomDirection(); 35 //TimeCountを0に戻す 36 timeCount = 0; 37 } 38 } 39} 40 41//直接directionを渡すのではなく、引数として渡す 42void Turn(Vector3 _direction) 43{ 44 //タイムカウントを0に戻す。 45 timeCount = 0; 46 if (_direction == Vector3.up) 47 { 48 transform.position = new Vector3(transform.position.x, maxMoveArea.y, transform.position.z); 49 direction = Vector3.down; 50 } 51 else if (_direction == Vector3.down) 52 { 53 transform.position = new Vector3(transform.position.x, minMoveArea.y, transform.position.z); 54 direction = Vector3.up; 55 } 56 else if (_direction == Vector3.left) 57 { 58 transform.position = new Vector3(minMoveArea.x, transform.position.y, transform.position.z); 59 direction = Vector3.right; 60 } 61 else if (_direction == Vector3.right) 62 { 63 transform.position = new Vector3(maxMoveArea.x, transform.position.y, transform.position.z); 64 direction = Vector3.left; 65 } 66 //追記 67 pos = transform.position; 68}

とすれば①の瞬間移動は解消できます。

②範囲の一番右側に当たると、範囲の上下を高速で行き来する

右端、というより右上や右下の角に入ると、ですね。
これも発生しました。
同じようにLogで見てみると、右にはみだしている(?)状態でdirection = Vector3.downとdirection = Vector3.upが交互に入れ替わっているみたいです。
これも①の後半と同様、ある意味瞬間移動なので、上のコードで解消できます。
これで無理な場合は、Turn()で修正する座標を境目から少しずらしてやればいいと思います。

上記のコードに直し、こちらでプログラムを10分間程度放置しました(はみだしは120回でした)が、問題は発生しませんでした。

長文失礼しました。

投稿2021/06/26 23:44

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

momonoki

2021/06/27 01:33

ご回答誠にありがとうございます。 質問後、自分でもコードをいじってみて、質問欄に追記したソースコードで一応意図した動作を見せるようになりました。学習の記録のために残しておきます。 >「Vector3 pos」にStart()のときしか値が格納されておらず、以後ずっとその値を参照するのでコード側からしたらNPCがどう動いて今どの座標にいるかに関係なく、ずっと範囲内だと認識します。 おっしゃる通りでした。ずっと、ワールド座標とローカル座標の違いか?など見当違いの推測をしておりました。追記し、範囲内で動くようになりました。 瞬間移動問題についてもいただいたコードを実行したところ解消できました!追記に貼った私のコードではTurn内で再度オブジェクトの位置を判定してから進む方向を定義していたのですが、引数で渡すやり方の方がスマートですね。 >これで無理な場合は、Turn()で修正する座標を境目から少しずらしてやればいいと思います。 おっしゃる通り、境目にオブジェクトが張り付いてしまう場合がありましたので、再配置のポジションを若干境目からずらしました。 全体的に意図した動作でプログラムが動き、プログラムに対する理解も深めることができました。丁寧なご回答ありがとうございます!
退会済みユーザー

退会済みユーザー

2021/06/27 01:35

解決出来ましたか!良かったです!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問