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

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

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

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

Unity

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

Q&A

解決済

1回答

1822閲覧

Unityの2Dアクションゲームで突進してくるエネミーのコードについて

cushionA

総合スコア90

C#

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

Unity

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

0グッド

0クリップ

投稿2020/03/04 12:02

編集2020/03/05 05:59

前提・実現したいこと

現在unityを使用して横スクロール2Dアクションゲームを制作しています。
そして画面内に入ると数秒の溜めの後地上を走って突進してくるエネミーを作ろうとしたところ、なかなかうまくいきません。
仕様としてはエネミーのvelocityをコルーチンで操作して動かすようなものです。
しかし、そもそも動かなかったり、それ以前の取り組みでは限りなく追尾してくるなど難航しています。

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

仕様としてはエネミーのvelocityをコルーチンで操作して動かすようなものです。
しかし、そもそも動かなかったり、それ以前の取り組みでは限りなく追尾してくるなど難航しています。

該当のソースコード

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Enemy3 : MonoBehaviour { #region//インスペクターで設定する [Header("移動速度")] public float speed; [Header("重力")] public float gravity; public GameObject player; #endregion float playerPos; #region//プライベート変数 private Rigidbody2D rb = null; private SpriteRenderer sr = null; private Animator anim = null; bool right; Transform rbs; int xVector; float chargeTime = 0.0f; #endregion // Start is called before the first frame update void Start() { rb = GetComponent<Rigidbody2D>(); sr = GetComponent<SpriteRenderer>(); anim = GetComponent<Animator>(); rbs = player.GetComponent<Transform>(); } void FixedUpdate() { anim.SetBool("walk", false); //デフォルトの立ちモーションに遷移。 if (sr.isVisible) { playerPos = rbs.position.x; if (playerPos - transform.position.x > 0) { right = true; } else { right = false; } //↑プレイヤーがどちらにいるか判断。 if (right) { transform.localScale = new Vector3(1, 1, 1); StartCoroutine(Charge(1)); //プレイヤーの向きにアニメーションを反転させ、引数を入れてコルーチン開始。 } //プレイヤー左にいる else { transform.localScale = new Vector3(-1, 1, 1); StartCoroutine(Charge(-1)); //プレイヤーの向きにアニメーションを反転させ、引数を入れてコルーチン開始。 } } } IEnumerator Charge(int xVector) {   chargeTime = 0;//---------------New! yield return new WaitForSeconds(3.0f); Debug.Log("コルーチン開始"); //三秒待って処理開始。 anim.SetBool("walk", true); rb.velocity = new Vector2(xVector * speed, -gravity);        Debug.Log("移動");//----------New! //アニメーションを移動に遷移させ、さらに引数の方向へ設定したスピードで動かす。 chargeTime += Time.deltaTime; if (chargeTime > 3.0f) { //二秒間突進して終了 Debug.Log("コルーチン終了");//---------------New! yield break; } } }

試したこと

MoveTowardsメソッドなども使用してみましたが、限りなく追尾してくるのでうまくいきません。
また、前述のメソッドはY軸方向にも動いてしまうので地上のみ移動してほしいという仕様に合いませんでした。
よいアイデアがあればどなたかご教授お願いします。

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

Unityの2019 2,9f1 personal
コーディングはVisualStudio2019にて行っています。

至らぬ点がありましたら追記修正させていただきます。
どうぞよろしくお願いします。

追記

コルーチンの始まりでチャージタイムを初期化するようにして、デバッグログに移動、コルーチン終了を加えました。(Newで示してあります)
各デバッグログの動作状況としては、コルーチン開始、移動は動作するものの、コルーチン終了のデバッグが動作していません。
また、それ以前の部分についてはスケールによる振り向きが遅滞なく行われるので、rightフラグの設定には問題はないものと思われます。
それからコルーチン内のspeedはパブリックメンバーのspeedと同じであることを確認し、それにインスペクタ上で10が格納されていることを確認いたしました。

情報が至らずご迷惑をおかけしました。
すみません。

インスペクタとデバッグログの表示です

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

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

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

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

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

sakura_hana

2020/03/05 01:22

Debug.Logを各所に入れ、どこまで正常に動作しているか、各変数がどんな値になっているか確認してください。そして質問文にその結果を記載してください。(現状だと"コルーチン開始"が正しく表示されているか等) 特にspeed値が0になっていないか確認してください。(速度0だと当然動きません)
cushionA

2020/03/05 04:01

コルーチン開始は動作を確認しています。 speedにはインスペスタ上にて10を渡してあります。 コードだけではなく作動状況を知らせることを怠ってしまいすみません。 後で他の場所にもdebugを入れて確認し、結果を追加いたします。 とりあえずの答えになってしまいましたがどうかお許しくださいませ。
cushionA

2020/03/05 04:12

それから、質問のコードはエネミーのコードの移動に関する部分のみについて切り取ったコードなのですが、最後のカッコが一つ多かったことに気がついたので訂正いたします。 すみませんでした
guest

回答1

0

ベストアンサー

コルーチンはループしません。
なのでchargeTime += Time.deltaTime;は一度しか呼ばれず、if (chargeTime > 3.0f)を満たすこともありません。
(ループさせたいならコルーチン内でwhileを使う等する必要があります)

よってchargeTime += Time.deltaTime;yield return new WaitForSeconds(○);に変更。
続くif文は取り除けば、移動後○秒待つ処理になります。
(更にその後必要に応じて速度を0にする等、終了後の処理を入れればよいかと)

投稿2020/03/05 05:33

sakura_hana

総合スコア11427

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

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

cushionA

2020/03/05 05:57

回答ありがとうございます。 なるほど、コルーチンでは時間加算が正常に機能しないのですね……。 無知ゆえに全く思い当たりませんでした、ありがとうございます。 おっしゃる通りしてみましたところコルーチン終了が作動しました。 しかし、やはりエネミーは動きません。 yield return new WaitForSeconds(○);だと処理の中断という形になりますので、コルーチン内で移動も一度?(きわめて短い間?)しか呼ばれてないのではと思い ```C# while (chargeTime > 3.0f) { rb.velocity = new Vector2(xVector * speed, -gravity); Debug.Log("移動"); chargeTime += Time.deltaTime; } ``` というコードも試してみたのですが、こちらもダメでした。 ![インスペクタとデバッグログの表示です](47fbcc2b46218d5d956ab56119b95629.png) こちらのようにかなり短いスパンでコルーチンが呼ばれているようなのですが、かたくなに移動しないのにはこの辺りの事情が絡んでいたりしないでしょうか。 ちなみに、アニメーションのフラグ制御はできているようで、こちらは正常に切り替わります。 移動だけが行われない状況です。 何度もすみませんが、もしまだお時間のあるようでしたらお力添えいただけるとありがたいです。
cushionA

2020/03/05 05:58

画像はこの欄では表示されないようですので、質問欄に追記いたします。 お手間をおかけします。
sakura_hana

2020/03/05 06:12

FixedUpdateは物理フレーム毎(デフォルトだと0.02秒毎)に呼ばれます。 (sr.isVisibleをどう制御しているか分からなかったのでスルーしていましたが)何もしていないなら、毎フレームStartCoroutineされることになるのでその分コルーチンが生成されて動作します。 まずはここを確認して、Chargeメソッドが終了するまでは再呼び出ししない等の制御を入れてください。 それはそれとして動かないというのは理由がよく分からないので、 コルーチンを使わずに移動させてみるなど試してみてください。 (重力や他のオブジェクトが影響している等が考えられます)
cushionA

2020/03/05 07:00 編集

ご意見大変ありがとうございます。 とりあえずコルーチンの最初で「isCor」というフラグを立てて、最後に切るようにしました。 そのフラグが立っている間はコルーチンをスタートしないようにしましたところ、コルーチンの濫造?が止まりました。 コルーチン中のループや、コルーチンの制御など、移動についてはまだ解決していませんが新たな見識を与えていただき大変助かっております。 話が変わりますが、コルーチンを使わずにというとwhile文を使って何とかするか、AddForceメソッドを使う方法を思いついたのですが、なるべくキャラクターの移動にはvelocityにてをくわえるかたちが望ましいと聞いたのでwhileで移動し続けるアプローチでやってみようと思います。 大変ありがとうございます。 本来ならこの時点でベストアンサーにすべきなのかもしれませんが、試行の中で疑問が生じたりしたときにもう少し知恵をお借りしてもよろしいでしょうか? 図々しいですが、よろしくお願いいたします。 すみません。
cushionA

2020/03/05 07:14 編集

重力については、ほかのエネミーで動作を確認済みですのである程度信頼できる数値ではないかと思っています。 オブジェクトに引っかかっている可能性については見てみます。 それから、isVisibleは画面内に存在する場合に作動いたします。 ありがとうございました。
sakura_hana

2020/03/05 08:00 編集

「コルーチンを使わずに移動させてみる」というのは、 移動しない原因がコルーチンにあるのか、移動のコードにあるのかを区別する為の処置です。 例えばFixedUpdateメソッド内で「rb.velocity = 〜」として動くならコルーチンが問題、動かないならこの速度変更のコードが悪いと切り分けて考えることが出来ます。 velocityの書き換えは、現実的な物理演算としてはおかしいものになります。whileループで用いたら余計にそうです。 (摩擦や衝突や慣性などの力を一切無視して「指定の速度になる」為。一方AddForceは「力を加える」ので比較的正しい物理演算になります) ただゲーム的な動き(=非現実的な動き)としてはありと言えばありなので、正しく理解した上で使うのが望ましいです。
cushionA

2020/03/05 08:15 編集

新たなフィールドを追加してコードを書いていましたが、移動開始のデバッグログから先が作動していないようです。 それ以前、照準とカウント開始については確認できました。 また、デバッグログこそ出るもののエネミーが方向転換を行わなくなりました。 エスパー質問のようになって申し訳ありませんが、サクラ様の目から見て下記のコードになにか誤謬は見当たりませんでしょうか? チャージタイムの加算を行う場所をwhileの外に持っていったり微力ながら手を尽くしましたがどうにもなりません。 #region//インスペクターで設定する [Header("移動速度")] public float speed; [Header("重力")] public float gravity; public GameObject player; #endregion float playerPos; #region//プライベート変数 private Rigidbody2D rb = null; private SpriteRenderer sr = null; private Animator anim = null; bool right; Transform rbs; int xVector; float chargeTime = 0.0f; //------追加したフィールド。 bool isCor; float actTime = 0.0f; void FixedUpdate() { if (!isCor)//このフラグが立っている間は方向転換をできない {     Debug.Log("照準"); playerPos = rbs.position.x; if (playerPos - transform.position.x > 0) { right = true; } else { right = false; } //↑プレイヤーがどちらにいるか判断。 } if (sr.isVisible) {        Debug.Log("カウント開始");     //画面に入っている間動作 float actTime = +Time.deltaTime; //時間を数えて、画面に入った三秒後に起動。また、突進後のクールタイムも兼ねる。 if (right && actTime > 3.0f) { //プレイヤーが右側、かつクールタイム消化。 Debug.Log("移動開始"); transform.localScale = new Vector3(1, 1, 1); xVector = 1; while (chargeTime < 3.0f) { //突進の制限時間三秒である以内は方向転換を禁じて進み続ける。 anim.SetBool("walk", true); isCor = true;//方向転換を禁止。 chargeTime += Time.deltaTime; rb.velocity = new Vector2(xVector * speed, -gravity); } } else if(!right && actTime > 3.0f) { //プレイヤーが左側、かつクールタイム消化。 transform.localScale = new Vector3(-1, 1, 1); xVector = -1; while (chargeTime < 3.0f) { //突進の制限時間三秒である以内は方向転換を禁じて進み続ける。 anim.SetBool("walk", true); Debug.Log("移動開始"); isCor = true;//方向転換を禁止。 chargeTime += Time.deltaTime; rb.velocity = new Vector2(xVector * speed, -gravity); } } if (chargeTime >= 3.0f) {//突進の制限時間を超えたら初期状態に戻し、方向転換を可能に。 anim.SetBool("walk", false); isCor = false; actTime = 0.0f; chargeTime = 0.0f; } }
cushionA

2020/03/05 08:06

すみません!今返信を見ました。 理解するので少しお待ちください。
cushionA

2020/03/05 08:13

なるほど。 そういうことでしたか。 理解が及ばず申し訳ありません。 移動のコードについては、ほかのエネミーとプレイヤーキャラが全てこの文で動かせているので、僕の制御に問題があるのだと思われます。 しかしとはいえ現在の僕ではVelocityで突進させるのは難しそうなのでAddForceに切り替えてみます。 Velocity以外でも特に問題はなく、用途に合わせて使い分けているだけなのだということをしっかりと意識していこうと思います。 ありがとうございました。
sakura_hana

2020/03/05 08:55

一応先程投稿されたコードについて。 まず「float actTime = +Time.deltaTime;」ここで新しい変数を定義している為、毎フレームTime.deltaTimeが代入されています。 なので「if (right && actTime > 3.0f)」も「else if(!right && actTime > 3.0f)」も満たさないのでその先の処理が行われません。 また、ここを正しく通過したとしても、 whileの中で待機していないので時間的には一瞬でループが終わります。 (速度を変更してそのままなので移動はしますが、すぐに方向転換可能になります) なのでコルーチン内でyieldを併用しつつwhileを回すか、 FixedUpdateは毎フレーム実行されることを理解した上で条件分岐して動作させる必要があります。 (「unity タイマー」とかでググると考え方の参考になるかと思います) 前述の通り、Debug.Logで値を見ると何が起きているか分かると思うのでお試しください。 なお、これは不具合とは直接関係無いのですが、FixedUpdateで使うならTime.deltaTimeではなくTime.fixedDeltaTimeを使いましょう。 前者はUpdateの更新間隔、後者はFixedUpdateの更新間隔です。両者は大抵ズレているので、適切な方を使わないと正しく秒数が測れません。 (例えば40fpsの時Time.deltaTimeは0.025、Time.fixedDeltaTimeは0.02です。0.02秒間隔で0.025を加算していき3になるのは、3秒後ではなく2.4秒後になります)
cushionA

2020/03/05 12:28

すみません!返信が遅れました……。 まさかそっちのコードまで見ていただけるとは思いませんでした、ありがとうございます。 実に半日もお付き合いさせてしまい申し訳ありません。 ゲームのためには絵の練習などもせねばならず、今日はもうプログラミングはしません。 ですが明日以降の活動に必ずや活かさせていただきます。 あともう一度くらいコルーチンとVelocityのアプローチで取り組んでみるつもりです。 それからテックアカデミーの記事でしょうか? Unityタイマーについても拝見させていただきました。 確かに様々な時間管理の方法があり、とてもためになりました。 本当にありがとうございます。
cushionA

2020/03/07 05:42

少し時間がかかりましたがおかげさまで実装できました。 いろんなことを学びましたが、特に時間管理の繊細さとwhile文の使い方には注意が必要だということを学べたような気がします。 本当にありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.49%

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

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

質問する

関連した質問