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

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

新規登録して質問してみよう
ただいま回答率
85.47%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

Q&A

解決済

3回答

1046閲覧

JavaScriptで音ゲーの判定処理を実装する

falcon_function

総合スコア6

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

HTML

HTMLとは、ウェブ上の文書を記述・作成するためのマークアップ言語のことです。文章の中に記述することで、文書の論理構造などを設定することができます。ハイパーリンクを設定できるハイパーテキストであり、画像・リスト・表などのデータファイルをリンクする情報に結びつけて情報を整理します。現在あるネットワーク上のほとんどのウェブページはHTMLで作成されています。

0グッド

0クリップ

投稿2023/06/30 11:05

実現したいこと

  • 音楽の再生時間に応じて、キーボードが押された時点の判定処理を実装したい
  • 処理速度の向上

前提

ソースコード内では、前回とは違い、キーボード(上キーのみ)・判定・音楽の取り込みと再生時間の取得とその表示に限定しています。

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

・setIntervalで10ms毎に処理をしている関係上遅くなってしまいます。1つのみならまだしも複数個ノーツが降ってくるとなると ミリ秒単位の遅延が重なってプレイがまともにできなくなってしまいます。 ・定数になっている部分( plytime - (10 + Math.abs(start-fin))のこと)が、ノーツが降ってくるタイミングです。 しかし、初期化に失敗すると指定秒数でなくても有効秒数(time <= 0.032など)内のメッセージが実行されてしまうバグが可能性としてあります。

該当のソースコード

HTML

1<!DOCTYPE html> 2<head> 3<title>再生時間表示のサンプル</title> 4</head> 5<body> 6<h1 id="display">Test</h1> 7<button id="playing">Play</button> 8<button id="stopped">Stop</button> 9<script> 10const display = document.getElementById("display"); 11const timedif = document.createElement("h2"); 12const button = document.getElementById("playing"); 13const stopped = document.getElementById("stopped"); 14const audio = new Audio("loop006.mp3"); 15let flags = false; 16let plytime; 17const param = { 18 totaltime: 0, 19 happened: 0, 20 start: 0, 21 end: 0, 22 bench_start: 0, 23 bench_end: 0 24} 25 26button.addEventListener("click",(event)=>{ 27 audio.volume = 0.05; 28 audio.playbackRate = 2; 29 audio.play(); 30 param.happend = performance.now()/1000; 31 console.log(`happend:${param.happend}`); 32 flags = true; 33}); 34 35 36stopped.addEventListener("click",(event)=>{ 37 audio.pause(); 38 audio.currentTime = 0; 39 flags = false; 40 delete check; 41 display.textContent = "0:00:00"; 42 plytime = null; 43 now = null; 44 param.totaltime = null; 45 param.happend = null; 46 param.end = null; 47 param.start = null; 48 param.bench_start = null; 49 param.bench_end = null; 50 delete playingtime; 51 52}); 53 54function playingtime(){ 55 if(flags){ 56 plytime = audio.currentTime; 57 display.textContent = plytime + "秒"; 58 param.end = performance.now() / 1000; 59 if(param.start){ 60 param.totaltime = Math.abs(param.end-param.start - plytime); 61 } 62 timedif.innerHTML = `${plytime}:${param.totaltime}`; 63 display.appendChild(timedif); 64 Judge(plytime,param.totaltime); 65 66 } 67} 68setInterval(playingtime,10); //こうしてしまうとローカルならまだしも、ネット上だと無条件で10msごとに実行するため、遅延+10msが無条件で実行されてしまう。 69class Pushing{ 70 constructor(up_push,down_push,left_push,right_push){ 71 this.up_push = false; 72 this,down_push = false; 73 this.left_push = false; 74 this.right_push = false; 75 } 76} 77const check = new Pushing(); 78const Judge=function(start,fin){ 79 param.bench_start = performance.now(); 80 if(flags){ 81 if(check.up_push){ 82 const timing = plytime - (10 + Math.abs(start-fin)); 83 if(timing <= 0.072 && timing > 0.032){ 84 console.log("GOOD timing"); 85 console.log(`plytime: ${start}/ timing: ${timing}`); 86 flags = false; //入れてみたが、音楽だけなり続け、他の処理が止まってしまうため意味をなさない。 87 console.log(`currenttime: ${param.bench_end - param.bench_start}`); 88 } 89 } 90 if(check.up_push){ 91 const timing = plytime - (10 + Math.abs(start-fin)); 92 if(timing<= 0.032 && timing >= 0){ 93 console.log("PERFECT timing"); 94 console.log(`plytime: ${start}/ totaltime: ${fin}`); 95 flags = false; //入れてみたが、音楽だけなり続け、他の処理が止まってしまうため意味をなさない。 96 console.log(`currenttime: ${param.bench_end - param.bench_start}`); 97 } 98 } 99 } 100 param.bench_end = performance.now(); 101} 102 103 104document.addEventListener("keydown",(event)=>{ 105 switch(event.key){ 106 case 'ArrowUp': 107 if(flags === true){ 108 check.up_push = true; 109 param.start = performance.now()/1000; 110 console.log(`keypushed: ${param.start}ms`) 111 } 112 break; 113 } 114}); 115 116document.addEventListener("keyup",(event)=>{ 117 switch(event.key){ 118 case 'ArrowUp': 119 if(check.up_push){ 120 check.up_push = false; 121 } 122 break; 123 } 124}); 125</script> 126</body> 127</html>

試したこと

・setIntervalでplayingtime関数を10msで処理し続けるようにした
・判定処理部分の実行速度を計測した。平均で0.3msかかった
・メモリリーク対策に、今はストップボタンを押したら、null代入とdeleteで削除するようにした

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

ブラウザ:Chrome 114.0.5735.134
PCのスペック: CPU: i7-12700 2.10 GHz
メモリ: 32GB
Windows11 Home 22H2

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

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

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

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

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

int32_t

2023/06/30 16:40

「ノーツ」が増えたからってそんなに遅くならない気がするのですけど、複数あるときにどういうコードになっているのでしょうか。 performance.now() はセキュリテイ対策のために正確な値を返さないようになっています。細かい数字は信用できません。 https://developer.mozilla.org/ja/docs/Web/API/Performance/now
falcon_function

2023/07/01 07:43

質問ありがとうございます。 考えている内容としては以下の二つです。 ・判定枠用クラス「ノーツレーン」には座標xy、加速度VelXVelY、画像幅と高さ、落ちてくる時間を受け取ります。ただしクラス「ノーツ」を継承しています。 複数個生成する際にはwhile(空の譜面データ用配列 < ノーツの総個数)でループさせようと思っています。 ・クラス「ノーツレーン」のlaneupdateメソッド内で今回のJudge関数で処理をさせることで複数個ノーツが来ても対応するようにします。 疑似コードも交えて記述するなら、 class notes_lane extends Notes{ constructor(x,y,VelX,VelY,width,height,time){ super(x,y,VelX,VelY,width,height,time); } laneupdate(){ while(空の譜面用配列 < ノーツの総数){ Judge(今現在の曲の再生時間,(キー入力時間-playingtimeの発生時間 - 現在の曲の再生時間)); } } } という仕組みにしたいです。
guest

回答3

0

具体的にどういうタイミングを判定したいのかアルゴリズムがよくわからないので一般的な助言になりますが、Judge() を無駄に呼んでいるような気がします。
キーが押されたタイミングを判定したいのなら keydown イベント内かその後一回だけ各ノーツに対して Judge() を呼べばいいはずです。今のコードはキーが押されてから離されるまでの間は毎回 Judge() を呼んでますよね。

  • setInternal() は画面描画とは関係ないタイミングでコードを実行するので、requestAnimationFrame() を使いましょう。
  • performance.now() の呼び出し自体もコストがかかるものなので、無闇に呼ばずに event.timeStamprequestAnimationFrame() コールバックの引数を活用しましょう。

投稿2023/07/02 14:34

int32_t

総合スコア20914

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

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

0

自己解決

自己解決いたしましたのでご報告いたします。
ご指摘していただけました通り、event.timeStampで取得したうえで、timeUpdateイベントで逐次取得し続けたところ処理速度が上がりました。ありがとうございます。

JavaScript

1<!DOCTYPE html> 2<head> 3<title>再生時間表示のサンプル</title> 4</head> 5<body> 6<h1 id="display">Test</h1> 7<button id="playing">Play</button> 8<button id="stopped">Stop</button> 9<script> 10const display = document.getElementById("display"); 11const timedif = document.createElement("h2"); 12const button = document.getElementById("playing"); 13const stopped = document.getElementById("stopped"); 14const audio = new Audio("loop006.mp3"); 15let flags = false; 16let plytime; 17const param = { 18 totaltime: 0, 19 happened: 0, 20 start: 0, 21 end: 0, 22 bench_start: 0, 23 bench_end: 0 24} 25 26button.addEventListener("click",(event)=>{ 27 audio.volume = 1; 28 audio.playbackRate = 1.2; 29 audio.play(); 30 param.happend = event.timeStamp /1000; 31 console.log(`happend:${param.happend}`); 32 flags = true; 33}); 34 35 36stopped.addEventListener("click",(event)=>{ 37 audio.pause(); 38 audio.currentTime = 0; 39 flags = false; 40 delete check; 41 display.textContent = "0:00:00"; 42 plytime = null; 43 now = null; 44 param.totaltime = null; 45 param.happend = null; 46 param.end = null; 47 param.start = null; 48 param.bench_start = null; 49 param.bench_end = null; 50}); 51function playingtime(event){ 52 if(flags){ 53 plytime = audio.currentTime; //ずっと代入し続けるというのが重要。 54 if(param.start){ 55 param.totaltime = Math.abs(param.end-param.start - plytime); 56 } 57 timedif.innerHTML = `${plytime}:${param.totaltime}`; 58 display.textContent = plytime + "秒"; 59 display.appendChild(timedif); 60 requestAnimationFrame(playingtime,event); 61 } 62} 63 64 65audio.addEventListener("timeupdate",(event)=>{ 66 if(flags){ 67 param.end = event.timeStamp / 1000; 68 requestAnimationFrame(playingtime,(param.end)); 69 } else { 70 cancelAnimationFrame(playingtime); 71 } 72}); 73 74class Pushing{ 75 constructor(up_push,down_push,left_push,right_push){ 76 this.up_push = false; 77 this,down_push = false; 78 this.left_push = false; 79 this.right_push = false; 80 } 81} 82const check = new Pushing(); 83const Judge=function(start,fin){ 84 const timing = Math.abs(10 - Math.abs(start-fin)); 85 if(flags){ 86 if(check.up_push){ 87 if(timing <= 0.072 && timing > 0.032){ 88 console.log("GOOD timing"); 89 console.log(`plytime: ${start}/ timing: ${timing}`); 90 flags = false; 91 audio.pause(); 92 audio.currenttime = 0; 93 console.log(`currenttime: ${param.bench_end - param.bench_start}`); 94 } 95 } 96 if(check.up_push){ 97 if(timing<= 0.032 && timing >= 0){ 98 console.log("PERFECT timing"); 99 console.log(`plytime: ${start}/ totaltime: ${fin}`); 100 flags = false; 101 audio.pause(); 102 audio.currenttime = 0; 103 console.log(`currenttime: ${param.bench_end - param.bench_start}`); 104 } 105 } 106 } 107} 108 109 110document.addEventListener("keydown",(event)=>{ 111 switch(event.key){ 112 case 'ArrowUp': 113 if(flags === true){ 114 check.up_push = true; 115 param.start = event.timeStamp /1000; 116 console.log(`keypushed: ${param.start}ms`); 117 Judge(plytime,param.totaltime); 118 } 119 break; 120 } 121}); 122 123document.addEventListener("keyup",(event)=>{ 124 switch(event.key){ 125 case 'ArrowUp': 126 if(check.up_push){ 127 check.up_push = false; 128 } 129 break; 130 } 131}); 132 133</script> 134 135 136</body> 137 138 139</html>

投稿2023/07/06 10:13

falcon_function

総合スコア6

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

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

0

requestAnimationFrame() を試さないんですか
setInterval() なんて普通に遅延が発生しますよね
知らんけど

投稿2023/06/30 11:21

soda346

総合スコア18

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

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

falcon_function

2023/06/30 13:40

一度試してみたのですが、クリックしてもなぜか反応しなくなってしまうのです playingtime関数のif文の中に1つ、そのすぐ下側に1つ入れても動作しなくなりました・・・
soda346

2023/06/30 17:07

それは基本的な使い方を間違えているだけでは? cancelAnimationFrame() を呼んでないとか 「反応しなくなる原因」を調べたんですか? 負荷が掛かっているからでしょ どこかで
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問