🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C#

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

Unity

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

Q&A

3回答

3070閲覧

<音ゲー開発>同時押しをすると片方のノーツが反応しない・後続のノーツが巻き込まれる

tuna-uniko

総合スコア10

C#

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

Unity

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

0グッド

0クリップ

投稿2021/01/14 09:55

編集2021/01/21 15:55

以前タイミングが同じ時ノーツを横並びにしたいと質問させて頂いた者です。質問タイトルが長くて恐縮ですが、以前から悩まされていることが二つあり、同時押しの時に対応するボタンを押しても片方が反応せず流れていってしまう問題と、16分など狭い間隔で同じボタンを2回連打する配置の時後続のノーツが巻き込まれて勝手に判定されてしまう問題です。

###起きている問題
・横一列に並んだノーツを同時押しで処理する際、片方が反応しないでそのまま画面外へ流れていってしまう
・16分のリズムでボタンを押す際、同じレーン上でこの操作を行うと後ろにあるノーツが勝手に判定されてしまう

###エラーメッセージなど
エラーメッセージはありませんが、反応しない原因は配列を使用してタイミングデータとノーツ番号を記録しており、一個判定されると次のノーツへ判定が移る仕組みを使用していることではないかと考えています。
□□と並んでいるとき、左側が先に生成される場合左のノーツに当てられた番号1を先に処理する流れとなっているため、同時のタイミングで押すと右側の2が割り当てられたノーツの処理をする流れになっていないせいで1だけ反応し2での無反応が起こるということです。
巻き込まれることについては、一瞬この判定プログラムを停止させることでどうにかしたいと考えています(同時に反応しているのではなく、後続のノーツは一瞬遅れて消えているため)

###目標
同時押しをする際、二つのノーツを同時に処理できるような組み方をしたいです。また処理部を一瞬停止させるなど追加の処理を駆使して後続のノーツを巻き込まないように調整したいです。

###使用コード

C#

1if(Input.GetKey(KeyCode.G) && noterean==0){ 2//左端のレーンにノーツがある時Gキーを押す 3 //(スコアを増やす・音を鳴らす・捌かれたノーツを消す) 4 a+=1; 5 //aは現在のノーツ番号を示しています 6} 7//以下別のレーン番号とキー全て同様の処理

開発環境 Unity2019.1.5f1 VisualStudio

<1/22追記> 実際の使用コードを載せさせて頂きます。

C#

1using System.Collections; 2using System.Collections.Generic; 3using UnityEngine; 4using UnityEngine.UI; 5 6public class rejudge : MonoBehaviour 7{ 8 //曲をセット 9 public GameObject mus1; 10 public GameObject mus2; 11 public GameObject mus3; 12 13 GameObject sc; 14 GameObject co; 15 GameObject cm; 16 GameObject notesystem; 17 GameObject aud; 18 GameObject han; 19 renotego goscript; 20 AudioSource to; 21 22 Animation hante; 23 24 int tes; 25 //ノーツ情報 26 float notetim; 27 int noterean; 28 int notetyp; 29 float timer; 30 31 int a, b; 32 33 public AudioClip don; 34 private AudioSource dk; 35 36 public static int sa = 0; //スコア 37 public static int cx = 0; //コンボ 38 public static int max; 39 public static int mima = 0; //ミス有での最大コンボ 40 public static int keij; 41 public static int soumiss; 42 public static int sougre; 43 public static int resa() 44 { 45 return sa; 46 } 47 public static int recx() 48 { 49 return cx; 50 } 51 public static int remima() 52 { 53 return mima; 54 } 55 public static int reju() 56 { 57 return keij; 58 } 59 public static int resou() 60 { 61 return soumiss; 62 } 63 public static int regre() 64 { 65 return sougre; 66 } 67 68 void Start() 69 { 70 tes = Selectmode.mode; 71 sa = 0; 72 cx = 0; 73 74 timer = 0; 75 soumiss = 0; 76 keij = 0; 77 sougre = 0; 78 79 //判定表示 80 han = GameObject.Find("判定A"); 81 hante = han.GetComponent<Animation>(); 82 han.GetComponent<Text>().text = ""; 83 84 //選択された曲の番号 85 b = Trackscrolle.selectedtracknumber; 86 87 88 notesystem = GameObject.Find("5Rmanager"); 89 goscript = notesystem.GetComponent<renotego>(); 90 a = goscript.f; 91 switch (b) 92 { 93 case 3: 94 aud = mus1; 95 break; 96 case 4: 97 aud = mus2; 98 break; 99 case 5: 100 aud = mus3; 101 break; 102 default: 103 Debug.Log("存在しない曲情報です"); 104 break; 105 } 106 to = aud.GetComponent<AudioSource>(); 107 108 sc = GameObject.Find("Score"); 109 GameObject sp = GameObject.Find("S"); 110 co = GameObject.Find("chainsuu"); 111 cm = GameObject.Find("conbo!"); 112 113 if (tes == 1) 114 { 115 sc.GetComponent<Text>().text = "-"; 116 } 117 118 co.SetActive(false); 119 cm.SetActive(false); 120 121 //効果音 122 dk = gameObject.GetComponent<AudioSource>(); 123 dk.clip = don; 124 } 125 126 void Update() 127 { 128 timer = to.GetComponent<AudioSource>().time; //曲開始からの時刻 129 float perfectnumMin = -0.2f; //パーフェクト判定エリアの上限(タイミング+この数値) 130 float perfectnumMax = 0; //パーフェクト判定エリアの下限(タイミング+この数値) 131 float okMin = -0.1f; //グレートエリア上状限(タイミング-(パーフェクト上限+この数値)) 132 float okMax = 0.1f; //グレートエリアの下限(タイミング+(パーフェクト下限+この数値)) 133 134 if (tes == 0) 135 { 136 notetim = goscript.seikaku[a];//読み込まれた譜面データをこちらにも移動 137 noterean = goscript.rea[a]; 138 notetyp = goscript.type[a]; 139 140 if (notetim + perfectnumMin < timer && timer < notetim + perfectnumMax) 141 //パーフェクト判定 142 { 143 if (Input.GetKey(KeyCode.G) && noterean == 0) //レーン番号と対応したキーの入力 144 { 145 sa += 130; 146 cx += 1; 147 dk.Play(); //clipの音を再生 148 keij += 1; 149 Destroy(goscript.noteGo[a]); 150 151 a += 1; 152 } 153 //以下H~Lキー全て同じ処理 154 155 if (cx > 4) 156 { 157 co.SetActive(true); 158 co.GetComponent<Text>().enabled = true; 159 cm.SetActive(true); 160 cm.GetComponent<Text>().enabled = true; 161 } 162 163 164 165 } 166 else if (notetim + perfectnumMin + okMin < timer && timer < notetim + perfectnumMax + okMax) //グレート判定 167 { 168 if (Input.GetKey(KeyCode.G) && noterean == 0) //レーン番号と対応したキーの入力 169 { 170 if (notetyp == 1) 171 { 172 sa += 70; 173 cx += 1; 174 dk.Play(); 175 sougre += 1; 176 han.GetComponent<Text>().text = "Great!"; 177 hante.Play(); 178 Destroy(goscript.noteGo[a]); 179 a += 1; 180 } 181 if (notetyp == 2) 182 { 183 sa += 130; 184 cx += 1; 185 dk.Play(); 186 keij += 1; 187 han.GetComponent<Text>().text = "Perfect!"; 188 hante.Play(); 189 Destroy(goscript.noteGo[a]); 190 a += 1; 191 } 192 } 193 //以下H~Lキーまで同一 194 if (cx > 4) 195 { 196 co.SetActive(true); 197 co.GetComponent<Text>().enabled = true; 198 cm.SetActive(true); 199 cm.GetComponent<Text>().enabled = true; 200 } 201 202 } 203 else if (notetim + perfectnumMax + okMax < timer) //ミス処理 204 { 205 mima = cx; 206 cx = 0; 207 han.GetComponent<Text>().text = "Miss"; 208 hante.Play(); 209 210 soumiss += 1; 211 a += 1; 212 213 } 214 215 sc.GetComponent<Text>().text = sa.ToString(); //スコア表示 216 co.GetComponent<Text>().text = cx.ToString(); //コンボ表示 217 218 } 219 220} 221

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

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

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

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

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

guest

回答3

0

コードが足りないという話ですが、その処理に必要な部分がって意味です。これでは不必要なものはたくさんありますが、判断するのに必要な部分がありません。

ここからは回答という内容ではなくなってしまうので不適切かもしれませんが書かせて頂きます。

まず、この状態ですと軽く読んだ感じでも音源を設定する、スコア(楽譜でなく点数)関係の処理?それらに付いてくるText関連など現在問題にしていない部分が多数含まれています。困っている部分はそこではありませんよね?関係ない部分があればあるほど簡単な内容でも難しくなります。

次にこの質問の回答の内容としての入力処理(バグがある可能性のある処理を行っている部分ですのでH~L同じでは当然ダメですよね?)同じですでバグとなればa+=1がやはり一番怪しい処理になります。

その中で使われているクラス、goscriptの内容も[a]という形で使用していますが、このコードでは判断できませんよね?入力に対する判定を行うためのデータに当たる部分なんだろうって事はなんとなく分かりますがseikaku[a]、rea[a]、type[a]には何が入っているのでしょう?

notetimにはfloat型で判別を行う時間的な何か?が入っているっぽい?
多分その値とタイミングを比較しているのでif文を意図しない形で通している、もしくは通すつもりのものが通っていない。
または、比較したい値がそこに入っていない。計算が間違っている、もしくは比較するものを間違えている。としか言いようがないのです。

次ですが、質問とは関係ない事でstaticがなぜそうなったのか分かりませんが、かなりおかしな使い方になってます。

public static int sa = 0; public static int resa() => sa; // 現在のunityのc#バージョンなら単一の処理はこの書き方=>で{}もreturnも省略できます

みたいな事になってますが、

public static int sa = 0; クラス名.sa;

staticはそもそも上記でアクセス出来ます。あなたのコードだとrejudge.saって事です。publicなstaticですので。rejudgeというクラスを複数生成してもsaは一つだけって形になります。

今書いている様な書き方をするのはいわゆるプロパティ、ゲッターとかセッターの話で

private int sa = 0; // プライベート public int Getsa() => sa; // saの値渡し、saの数値だけ渡して上記のプライベートなsaの値は変更されない public void Setsa(int sa) => this.sa = sa; // メソッドを呼び出してthis(自身の).saに代入する // 上記はクラス名.Getsa()で使いますが、下記はクラス名.saで使います。別のクラス(インスタンスで)saの中身を変更(sa = ~)みたいな書き方をすれば変更できません!(privateな変数にはアクセス出来ません、みたいな)エラーがでます public int sa {get; private set;} // これも省略してこう書けますってヤツです

ちなみに参照を渡すとまた違った事になりますので上記コメントは間違いですので無視してください。この場合はそうなるってだけです。

省略する書き方は知っておいて損はないと思います。ある程度分かっている人はこういう書き方を多用します。ネットで調べてもそういう書き方は多いです。そのほうが情報量が少なくなる(見える範囲内に収まりやすくなり、コードの量も減る)ので。

質問がしっかりしていないと、こういう質問の意図とは関係ない内容も返ってきます。多分、意味不明と思われたら普通は回答が来ません。一度回答してしまったのである程度答えを返さないのは失礼かと思いましたので。

勉強不足だから勉強しましょうって事になっちゃうんですけどこういうのを面白いと思えるか?ってとこです。面白いと思えるならとことんいきましょう

あと、見せていただいたコードからすくなくとも簡単な所からでいいのでメソッド化した方が良いと思います。多分ですけど、そういった事もまだ分からないのでは?と思います。

質問欄のコードより

if (notetyp == 1) { Test(70, "Great!"); sougre += 1; } private void Test(int score, string text) { sa += score; cx += 1; dk.Play(); han.GetComponent<Text>().text = text; hante.Play(); Destroy(goscript.noteGo[a]); a += 1; }

こうやって適当な名前(今回はTest)を付けて、if文の中身をメソッド化(関数化)して分離するだけでもだいぶ見通しが良くなります。

例として、if (notetyp == 2)の場合だと、上記メソッドを利用して

if (notetyp == 2) { Test(130, "perfect!!"); keij += 1; // これは良くわからんので残しました }

みたいな感じで簡潔になります。そうやってある程度細かくできればバグが発生しても、このコードがおかしい!この辺りで悪さしてる数値があるなんてのが分かりやすくなります。

投稿2021/01/22 08:04

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

同時押し判定ができない理由はGが押された時点でa++され、それ以下の処理では次のノートを見てしまっているから、コードだけで見ると考えている通りだと思われます。

例えばとして、適当ですがコードも提示します。

現状だとコード部分の要素が足りなさすぎますので勝手に解釈しての回答になります。
どの程度まで使って書いてよいかも分からないので配列で記述します。
適当なので汚いですが、コメントは添えておきます。

using System.Collections; using System.Collections.Generic; using UnityEngine; public class Test : MonoBehaviour { // 音楽を時間でとらえてTime.deltaTimeで考える事にします // この方法で実際に音楽とゲーム処理の同期が取れるかは分かりません public float timeCount; // 音符にあたる部分、対応するキー、時間で配列にしています // 形としてキーはintですが面倒なのでfloatで整数とします float[,] note; // キーに対応するノート番号 int[] current; // ざっくり判定に使用しています // judge、この秒より離れていれば判定対象にならない // badTiming、ノートからこの秒以内ならbad判定 // goodTiming、ノートからこの秒以内ならgood判定 float judge = 2.0f; float badTiming = 1.0f; float goodTiming = 0.5f; private void Start() { // 初期化、ノート配列の生成です current = new int[4] { 0, 0, 0, 0 }; note = new float[4, 10] { { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }, // Aに対応 { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }, // Sに対応 { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }, // Dに対応 { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 } // Fに対応 }; timeCount = 0; } private void Update() { // ループさせるとoutofrangeエラーが大量発生しますので処理を15秒で止めときます if (timeCount > 15.0f) return; // 入力値の初期化 float[] inputTiming = new float[4] { 0, 0, 0, 0 }; // 入力があればスタートしてからの時間を取得します if (Input.GetKeyDown(KeyCode.A)) inputTiming[0] = timeCount; if (Input.GetKeyDown(KeyCode.S)) inputTiming[1] = timeCount; if (Input.GetKeyDown(KeyCode.D)) inputTiming[2] = timeCount; if (Input.GetKeyDown(KeyCode.F)) inputTiming[3] = timeCount; // キーの回数分、キーに対して同じ処理を行います for(int i = 0; i < 4; i++) { // キーに対する入力がない(0)なら次のキーへ、あまり良い比較の仕方ではありませんが if (inputTiming[i] == 0) continue; // マイナスとプラスの値で処理を書くのも面倒なのでノート位置からの正の数値に変換 // 入力された時間と現在のキーの離れた秒数です var val = Mathf.Abs(inputTiming[i] - note[i, current[i]]); // judgeより大きければ判定外です if (val > judge) continue; // goodより小さいならgood判定 if (val < goodTiming) { Debug.Log("Good"); } // good判定にもれたらbad判定 else if (val < badTiming) { Debug.Log("Bad"); } // 入力判定後なのでそれ以外ならmiss判定 else { Debug.Log("Miss"); } // 判定は受けているので次のノートへ current[i]++; } // 入力が無かった場合の処理 for(int i = 0; i < 4; i++) { // badTimingを通り過ぎたら入力なしのmiss判定 if (note[i, current[i]] < timeCount - badTiming) { Debug.Log("Miss through"); current[i]++; } } // 時間経過 timeCount += Time.deltaTime; } }

実際に作るとすればノートはクラスで判定基準もノートごとに(固定値だと速いノートだと判定が被るので判定の自体の値を別に持つ、もしくは返すメソッドを用意するなど)、
入力データもクラス化して持つ方が楽(鍵盤を増やしたくなったとか、特殊な判定を作りたいとか)だと思います。

投稿2021/01/20 07:14

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

tuna-uniko

2021/01/21 15:58

mrmthdtm様 ありがとうございます。『コード部分の要素が少なすぎる』とのことなので、実際の使用コードを追記させて頂きました。 実際に参考にしながら書いてみたものの、最初の数フレームで判定処理が全て行われていました。また通過によるミス判定が毎フレーム呼ばれてしまいます。
guest

0

文法に間違いが無ければエラーは当然出ません、思った動作をしないだけです。

巻き込まれるっていうのは、近すぎて次のノーツがミス扱いになる、って事ですかね?

音ゲーとか全くですが、ノーツ1つにつき別々に処理を走らせる仕様に変更するとかどうでしょうか?、、、それなら同時押しにも対応できますし、同じレーンの次のノーツは判定を無視って処理も簡単に書ける気がします。

投稿2021/01/16 05:37

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

tuna-uniko

2021/01/19 23:49

mrmthdtm様 回答ありがとうございます。 ・巻き込まれるっていうのは~ 後続のノーツが一つ下の判定であるGreatで一緒に判定されてしまうことです。もしかしたらGetButtonで処理しているために押している間の判定がされて巻き込まれている可能性がある(一回押すだけで複数回処理されるため)ので、Downに修正してみたいと思います。 ・ノーツ1つにつき別々に~ 実装してみたいのですが上手くイメージできなかったため、もう少し詳しく教えて頂けないでしょうか。 よろしくお願いします。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問