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

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

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

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

Q&A

解決済

3回答

3863閲覧

弾幕STGで、綺麗なn-Way弾、全方位弾を作りたい

metalnickel

総合スコア2

C#

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

0グッド

1クリップ

投稿2020/07/24 11:24

編集2020/07/24 11:59

実現したいこと

弾幕STGで、弾を発射・動かすスクリプトがおよそ完成したので、
次のステップとして、n-Way弾や全方位弾などの関数を作ろうとしています。
実現したいものは、基本的な直線加減速だけの関数です。
下記にn-Way弾の関数を示します。

c#

1 ///<summary> 2 ///nWay弾を発射します。 3 ///<param name="BulletID">弾ID</param> 4 ///<param name="color">弾の色</param> 5 ///<param name="shotPosition">生成座標</param> 6 ///<param name="z">弾の初期z座標(表示順を調整)</param> 7 ///<param name="speed">初速</param> 8 ///<param name="baseAngle">基準角度(°)</param> 9 ///<param name="accel">加速度</param> 10 ///<param name="limitSpeed">最高・最低速度(加速度0の時は無効)</param> 11 ///<param name="n">way数</param> 12 ///<param name="angSpace">角度間隔</param> 13 ///</summary> 14 public static void ShootNway(int BulletID,string color,Vector2 shotPosition,float z,int speed, 15 float baseAngle,int accel,int limitSpeed,int n,float angSpace){ 16 if(n<=0){ 17 Debug.Log("way数が0以下になっています"); 18 return; 19 } 20 if(n>=1){ 21 for(int i=0;i<n;i++){ 22 float ang_i = baseAngle+(angSpace*0.5f)*(2*i-n+1); 23 float z_i = z+Time.deltaTime*i; 24         //CreateShot01は加減速可能な直進弾を生成する関数です。 25 CreateShot01(BulletID,color,shotPosition,z_i,speed,ang_i,accel,limitSpeed); 26 } 27 return; 28 } 29 }

発生している問題

加減速するようにすると、一部の弾の速度がずれ、弾幕の形状が崩れる問題が発生しています。
フレームで動かして確認した限り、ズレている弾だけ、1回目の速度更新が行われていない様子でした。
崩れたn-Way弾の図

該当のソースコード

上記のShootNwayではなく、速度を更新する下記ModifyDirection関数に問題があるのではないかと思っています。
速度(と角度)をint型で保存し、角度から作った単位ベクトルに速度をかける形です。

c#

1 public int tmpSpd;//更新される速度 2 public int accel;//加速度 3 public int limitSpeed;//最高/最低速度 4 5 public override Vector2 ModifyDirection(Vector2 pos, Vector2 dir){ 6 Vector2 vec = tmpAng.DegToVector() * tmpSpd; 7 //速度を更新 8 if(accel>0){//加速の時 9 if(tmpSpd<limitSpeed){ 10 tmpSpd += accel; 11 if(tmpSpd>limitSpeed){ 12 tmpSpd = limitSpeed; 13 } 14 } 15 }else if(accel<0){//減速の時 16 if(tmpSpd>limitSpeed){ 17 tmpSpd += accel; 18 if(tmpSpd<limitSpeed){ 19 tmpSpd = limitSpeed; 20 } 21 } 22 } 23 //角度を更新 24 tmpAng += angVel*Time.deltaTime; 25 return vec; 26 }

試したこと

①当初はfloat型による浮動小数点誤差かと思い、int型でも問題ないことを確認して切り替えましたが、改善しませんでした。
②初速(=発射の瞬間)のtmpSpdはデバッグですべて同じことを確認しています。
③発射直後(=発射の次のフレーム)では、ズレている弾の速度が加速度1個分だけ速いことがわかっています。(減速の場合)
(加速の場合は、逆に加速度1個分だけ遅いです。)

以上、どうぞよろしくお願いします。

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

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

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

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

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

bboydaisuke

2020/07/24 11:29

どのようなものを作りたいのかわからないし、現状がそれとどう違うのかもわからないので絵とかで説明してください。それと、以下の用語がわかりません。 ・拡張関数 ・加速度1個分
bboydaisuke

2020/07/24 11:32 編集

> 拡張関数 ちなみに C# では拡張メソッドというものがありますが、質問内容からしてそれとは明らかに違うものだと判断しています。つまり、難しい言葉を使ってみたら既にある用語と偶然一致してしまったため意味不明になっているんだろうと思っています。
metalnickel

2020/07/24 12:06

・作りたいものの図を追加しました。 ・拡張関数は自分で便宜上使っているだけでした。拡張メソッドとは関係ありません。 ・加速度1個分は下記に具体例を示します。 (例)初速500、加速度-20(=減速)のn-Way弾の場合 ①発射の瞬間、n個の弾は速度500です。 ②次フレーム、全ての弾は加速度-20を加算して速度480になるはずです。 ③実際には、n個の内数個だけ速度500のままになっていました(加速度1個分速い、の意味です)。 ④その次のフレーム以降は、ズレた弾も正常な弾と同様に減速します。 ⑤しかし、最低速度に到達するフレームがズレるため、弾幕の形状が添付図のように崩れてしまいます。
guest

回答3

0

自己解決

自己解決しました。
弾内時間がのズレが影響していた模様で、
弾の初期化関数に「弾内時間=0f」を入れたらズレる気配がなくなりました。

弾のリセット関数に「弾内時間=0f」が入っていたので問題ないと思っていましたが、
完全リセット出来ていなかったようです。

投稿2020/07/25 03:47

metalnickel

総合スコア2

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

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

0

単純に考えれば1Fで処理が終わっていない可能性がありますが、同期処理等は行っていますか?
可能であればフレーム数を一旦下げた場合に想定通りの動きをするか確認しましょう。

処理速度の問題であれば、チューニングするか、同期処理を入れて計算が終わるまでフレームを遅らせるしかないでしょう。

投稿2020/07/24 13:43

Kaleidoscope

総合スコア257

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

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

metalnickel

2020/07/24 14:07

回答ありがとうございます。 同期処理は初めて聞きまして、調べてもよくわからなかったので、宜しければご教授願えませんでしょうか。 フレーム数の落とし方もヒットしなかったので、できればよろしくお願いします。
metalnickel

2020/07/24 17:21

追加情報です。 ①弾の同一フレーム処理を試しました。 弾はすべてオブジェクトプールの子オブジェクトなので、プールのFixedUpdate関数が、アクティブな弾の代理Update関数を動かす、という試みをしましたが、解決しませんでした。 ②弾を再利用する場合に、ズレが発生することがわかりました。 使用履歴がある弾を発射する際に、ズレが起こる場合があるようです。 (例)1000発用意して、1回に100発撃つとしたら、10回目までは正常で、11回目以降にズレた弾が現れます。(プールは、使用していないものから使うようになっています) 弾のリセット、もしくはプールから弾を借りる時に問題があるのでしょうか?
guest

0

質問に書いていないところで同一フレーム内で処理できていないのでしょう。多分複雑になりすぎて問題がどこにあるか特定できず、全部を示したら長くなりすぎ、かつ全部示したとしても他の人にはわからないものになっているんじゃないかと思います。よくあることです。

いろんなやり方がありますが...一例としてシンプルで間違いのない作り方を示しておきます。

  1. 単発で勝手に飛んでいく弾のプレハブ a を作る
  2. a を複数、違う角度で飛ばすプレハブ b を作る
  3. プレハブ b を Instantiate する

イメージ説明
イメージ説明
イメージ説明

csharp

1using UnityEngine; 2 3[RequireComponent(typeof(Rigidbody2D))] 4public class SingleBulletController : MonoBehaviour 5{ 6 [SerializeField] float m_coefficient = 0.99f; 7 [SerializeField] float m_lifeTime = 1.5f; 8 [SerializeField] Vector2 m_direction = Vector2.zero; 9 [SerializeField] float m_speed = 4f; 10 Rigidbody2D m_rb; 11 float m_timer; 12 13 void Start() 14 { 15 m_rb = GetComponent<Rigidbody2D>(); 16 m_rb.velocity = m_direction.normalized * m_speed; 17 } 18 19 void Update() 20 { 21 m_rb.velocity *= m_coefficient; 22 m_timer += Time.deltaTime; 23 24 if (m_timer > m_lifeTime) 25 { 26 Destroy(gameObject); 27 } 28 } 29 30 public void SetDirection(Vector2 dir) 31 { 32 m_direction = dir; 33 } 34 35 public void SetSpeed(float speed) 36 { 37 m_speed = speed; 38 } 39}

csharp

1using UnityEngine; 2 3public class MultiBulletController : MonoBehaviour 4{ 5 [SerializeField] GameObject m_bulletPrefab; 6 [SerializeField] int m_count = 5; 7 [SerializeField] float m_angle = 5f; 8 [SerializeField] Vector2 m_direction = Vector2.up; 9 10 void Start() 11 { 12 Init(); 13 Destroy(gameObject); 14 } 15 16 void Init() 17 { 18 for (int i = 0; i < m_count; i++) 19 { 20 GameObject go = Instantiate(m_bulletPrefab, transform.position, Quaternion.identity); 21 SingleBulletController bullet = go.GetComponent<SingleBulletController>(); 22 23 if (bullet) 24 { 25 float angle; 26 27 if (i % 2 == 0) 28 { 29 angle = -1 * m_angle * ((i + 1) / 2); 30 } 31 else 32 { 33 angle = m_angle * ((i + 2) / 2); 34 } 35 36 Vector2 dir = Quaternion.Euler(0, 0, angle) * m_direction; 37 bullet.SetDirection(dir); 38 } 39 else 40 { 41 Debug.LogErrorFormat("Invalid bullet prefab: {0}", m_bulletPrefab.name); 42 } 43 } 44 } 45}

こういう作り方をしておけば、コードを変えなくても設定だけで以下のように方向や速さを変えられます。

イメージ説明

投稿2020/07/24 13:39

編集2020/07/24 14:06
bboydaisuke

総合スコア5275

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

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

metalnickel

2020/07/24 14:19

回答ありがとうございます。 オブジェクトプールに敵弾は保存してありまして、そこから呼び出して使用しています。 弾自体は内部に持っている進行ベクトルで勝手に移動するようになっており、 プールから呼び出すときに角度、速度、加速度を与えています。 加速は弾の中で計算させていますが、なぜか一部だけズレてしまう模様なのです。
bboydaisuke

2020/07/24 14:49

その「なぜか」という表現を排除しないといけないですね。問題が起きるケースと起きないケースを見つけて、どこが違うのかという範囲を少しずつ狭めていかないといけないですね。
metalnickel

2020/07/24 17:21

追加情報です。 ①弾の同一フレーム処理を試しました。 弾はすべてオブジェクトプールの子オブジェクトなので、プールのFixedUpdate関数が、アクティブな弾の代理Update関数を動かす、という試みをしましたが、解決しませんでした。 ②弾を再利用する場合に、ズレが発生することがわかりました。 使用履歴がある弾を発射する際に、ズレが起こる場合があるようです。 (例)1000発用意して、1回に100発撃つとしたら、10回目までは正常で、11回目以降にズレた弾が現れます。(プールは、使用していないものから使うようになっています) 弾のリセット、もしくはプールから弾を借りる時に問題があるのでしょうか?
bboydaisuke

2020/07/24 17:24

ここは誰も見ないと思うので、質問の方を編集した方がいいですよ。 > 弾のリセット、もしくはプールから弾を借りる時に問題があるのでしょうか? それはわかりません。それを排除するように書き変えてどうなるか確認してください。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問