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

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

ただいまの
回答率

87.90%

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

受付中

回答 3

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 725

score 10

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

起きている問題

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

エラーメッセージなど

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

目標

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

使用コード

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

開発環境 Unity2019.1.5f1 VisualStudio

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

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class rejudge : MonoBehaviour
{
    //曲をセット
    public GameObject mus1;
    public GameObject mus2;
    public GameObject mus3;

    GameObject sc;
    GameObject co;
    GameObject cm;
    GameObject notesystem;
    GameObject aud;
    GameObject han;
    renotego goscript;
    AudioSource to;

    Animation hante;

    int tes;
    //ノーツ情報
    float notetim;
    int noterean;
    int notetyp;
    float timer;

    int a, b;

    public AudioClip don;
    private AudioSource dk;

    public static int sa = 0; //スコア
    public static int cx = 0; //コンボ
    public static int max;
    public static int mima = 0; //ミス有での最大コンボ
    public static int keij;
    public static int soumiss;
    public static int sougre;
    public static int resa()
    {
        return sa;
    }
    public static int recx()
    {
        return cx;
    }
    public static int remima()
    {
        return mima;
    }
    public static int reju()
    {
        return keij;
    }
    public static int resou()
    {
        return soumiss;
    }
    public static int regre()
    {
        return sougre;
    }

    void Start()
    {
        tes = Selectmode.mode;
        sa = 0;
        cx = 0;

        timer = 0;
        soumiss = 0;
        keij = 0;
        sougre = 0;

        //判定表示
        han = GameObject.Find("判定A");
        hante = han.GetComponent<Animation>();
        han.GetComponent<Text>().text = "";

        //選択された曲の番号
        b = Trackscrolle.selectedtracknumber;


        notesystem = GameObject.Find("5Rmanager");
        goscript = notesystem.GetComponent<renotego>();
        a = goscript.f;
        switch (b)
        {
            case 3:
                aud = mus1;
                break;
            case 4:
                aud = mus2;
                break;
            case 5:
                aud = mus3;
                break;
            default:
                Debug.Log("存在しない曲情報です");
                break;
        }
        to = aud.GetComponent<AudioSource>();

        sc = GameObject.Find("Score");
        GameObject sp = GameObject.Find("S");
        co = GameObject.Find("chainsuu");
        cm = GameObject.Find("conbo!");

        if (tes == 1)
        {
            sc.GetComponent<Text>().text = "-";
        }

        co.SetActive(false);
        cm.SetActive(false);

        //効果音
        dk = gameObject.GetComponent<AudioSource>();
        dk.clip = don;
    }

    void Update()
    {
        timer = to.GetComponent<AudioSource>().time; //曲開始からの時刻
        float perfectnumMin = -0.2f; //パーフェクト判定エリアの上限(タイミング+この数値)
        float perfectnumMax = 0; //パーフェクト判定エリアの下限(タイミング+この数値)
        float okMin = -0.1f; //グレートエリア上状限(タイミング-(パーフェクト上限+この数値))
        float okMax = 0.1f; //グレートエリアの下限(タイミング+(パーフェクト下限+この数値))

        if (tes == 0)
        {
            notetim = goscript.seikaku[a];//読み込まれた譜面データをこちらにも移動
            noterean = goscript.rea[a];
            notetyp = goscript.type[a];

            if (notetim + perfectnumMin < timer && timer < notetim + perfectnumMax)
            //パーフェクト判定
            {
                if (Input.GetKey(KeyCode.G) && noterean == 0) //レーン番号と対応したキーの入力
                {
                    sa += 130;
                    cx += 1;
                    dk.Play(); //clipの音を再生
                    keij += 1;
                    Destroy(goscript.noteGo[a]);

                    a += 1;
                }
                //以下H~Lキー全て同じ処理

                if (cx > 4)
                {
                    co.SetActive(true);
                    co.GetComponent<Text>().enabled = true;
                    cm.SetActive(true);
                    cm.GetComponent<Text>().enabled = true;
                }



            }
            else if (notetim + perfectnumMin + okMin < timer && timer < notetim + perfectnumMax + okMax) //グレート判定
            {
                if (Input.GetKey(KeyCode.G) && noterean == 0) //レーン番号と対応したキーの入力
                {
                    if (notetyp == 1)
                    {
                        sa += 70;
                        cx += 1;
                        dk.Play();
                        sougre += 1;
                        han.GetComponent<Text>().text = "Great!";
                        hante.Play();
                        Destroy(goscript.noteGo[a]);
                        a += 1;
                    }
                    if (notetyp == 2)
                    {
                        sa += 130;
                        cx += 1;
                        dk.Play();
                        keij += 1;
                        han.GetComponent<Text>().text = "Perfect!";
                        hante.Play();
                        Destroy(goscript.noteGo[a]);
                        a += 1;
                    }
                }
                //以下H~Lキーまで同一
            if (cx > 4)
            {
                co.SetActive(true);
                co.GetComponent<Text>().enabled = true;
                cm.SetActive(true);
                cm.GetComponent<Text>().enabled = true;
            }

        }
        else if (notetim + perfectnumMax + okMax < timer) //ミス処理
        {
            mima = cx;
            cx = 0;
            han.GetComponent<Text>().text = "Miss";
            hante.Play();

            soumiss += 1;
            a += 1;

        }

        sc.GetComponent<Text>().text = sa.ToString(); //スコア表示
        co.GetComponent<Text>().text = cx.ToString(); //コンボ表示

    }

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

0

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

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2021/01/20 08:49

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

    キャンセル

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/22 00:58

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

    キャンセル

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; // これは良くわからんので残しました
}

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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

  • ただいまの回答率 87.90%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • トップ
  • C#に関する質問
  • <音ゲー開発>同時押しをすると片方のノーツが反応しない・後続のノーツが巻き込まれる