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

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

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

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

HTML

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

Q&A

解決済

2回答

471閲覧

JavaScriptで譜面データ管理の実装

falcon_function

総合スコア10

JavaScript

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

HTML

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

0グッド

0クリップ

投稿2023/07/06 11:30

実現したいこと

  • 譜面データを配列で管理したい
  • 描画処理の高速化

前提

クラス内のメソッドを呼び出すにはインスタンスを作成しないと動作しない。
今までのはconst testなど単体だったが、実際の音ゲーは複数ノーツが同時に降ってくるためその実装方法だと処理面でも厳しい。

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

・前提にもある通り、前回の質問(https://teratail.com/questions/qyt4yv84nqfgcf)で、loop関数内でただ単にconst testと単体インスタンスを呼び出してから、クラスNotes内を呼び出していました。 これを配列で管理しようと思っております。しかし、loop関数内で配列の値を読みだそうとすると処理落ちしてしまいます。 ・原因は毎回class Notesのデータを読みだそうとしているというのはわかるのですが、この頻度を下げる方法がわかりません。 ノーツの座標もしくは時間で制御すればよさそうですが、どのように実装したらよいのかわからずじまいです。

該当のソースコード

JavaScript

1let canvas = document.querySelector(".fnfcanvas") 2let ctx = canvas.getContext("2d"); 3let g_width = canvas.width = 1080; 4let g_height = canvas.height = 680; 5let posy = 0; 6let sprite = 0; 7let flags = false; 8let note_sprite = new Image(); 9const audio = new Audio("sample.mp3"); 10 11//ノーツの種類(上から通常・半透明・ダメージノーツ・即死ノーツ) 12const param={ 13 totaltime: 0, 14 start: 0, 15 end: 0 16} 17 18class Notes_Elements{ 19 constructor(){ 20 this.normal = 'normal'; 21 this.invisible = 'invisible'; 22 this.reverse = 'damage'; 23 this.death = 'death'; 24 } 25} 26const Judge={ 27 PERFECT: 0.032, 28 GOOD: 0.070, 29 BAD: 1.1, 30 MISS: 1.5 31}; 32class Pushing{ 33 constructor(up_push,down_push,left_push,right_push){ 34 this.up_push = false; 35 this,down_push = false; 36 this.left_push = false; 37 this.right_push = false; 38 } 39} 40class Pushing_continue extends Pushing{ 41 constructor(up_pushing,down_pushing,left_pushing,right_pushing){ 42 super(up_pushing,down_pushing,left_pushing,right_pushing); 43 } 44} 45 46 47window.addEventListener("click",(event)=>{ 48 audio.volume = 0.5; 49 audio.playbackRate = 1.2; 50 audio.play(); 51 flags = true; 52}); 53 54function playingtime(event){ 55 if(flags){ 56 let plytime = audio.currentTime; 57 if(param.start){ 58 param.totaltime = Math.abs(param.end-param.start - plytime); 59 } 60 requestAnimationFrame(playingtime,event); 61 } 62} 63audio.addEventListener("timeupdate",(event)=>{ 64 if(flags){ 65 param.end=event.timeStamp / 1000; 66 requestAnimationFrame(playingtime,(param.end)); 67 } else { 68 cancelAnimationFrame(playingtime); 69 } 70}); 71 72 73 74//ノーツそのものの制御(座標・加速度・画像の大きさ・各秒数) 75class Notes{ 76 constructor(x,y,VelX,VelY,width,height,time){ 77 this.x = x; 78 this.y = y; 79 this.VelX = VelX; 80 this.VelY = VelY; 81 this.width = width; 82 this.height = height; 83 this.time = time; 84 } 85 notesdrawing(x,y,VelX,VelY,width,height,time){ 86 //ここに描画処理を書く。 87 ctx.clearRect(0,0,width,height); 88 ctx.translate(width/2,height/2); 89 note_sprite.src = "arrow_down_empty.png"; 90 ctx.fillRect(-(width/2), -(height/2), width, height); 91 note_sprite.addEventListener("load",(event)=>{ 92 ctx.clearRect(0,0,g_width,g_height); 93 ctx.drawImage(note_sprite,(sprite * 220),0,158,157,110,0+posy,158,157); 94 }); 95 if (posy > g_height) { 96 const newStartPos = -((g_height/2) + 120); 97 posy = Math.ceil(newStartPos); 98 } else { 99 posy += 2; 100 } 101 } 102 103 notesmoving(x,y,VelX,VelY,width,height,time){ 104 //ここにVelYの挙動およびVelXなどの挙動を書く。 105 if(this.y + this.height >= g_height ){ 106 this.VelY = -(this.VelY); 107 } 108 if((this.y - this.height) <= 0){ 109 this.VelY = -(this.velY); 110 } 111 this.y += this.VelY; 112 113 } 114} 115 116const notes_LEGENDALY = []; 117//const notes_LASO = []; 118//const notes_SLASO = []; 119const test = new Notes(110,-100,1,4,110,110,34.5); 120for(let i = 0; i < 2000;i++){ 121 notes_LEGENDALY.push(test); 122} 123i = null; 124 125 126function loop(){ 127 /*問題箇所、このコメントアウトを外して実行する際は自己責任でお願いします。 128 notes_LEGENDALY.forEach((elements,index)=>{ 129 const notesData = new Notes(elements.x,elements.y,elements.VelX,elements.VelY,elements.width,elements.height,elements.time); 130 notesData.notesdrawing(); 131 notesData.notesmoving(); 132 }); 133 */ 134   test.notesdrawing(); 135 test.notesmoving(); 136 requestAnimationFrame(loop); 137} 138requestAnimationFrame(loop); 139 140class notes_lane extends Notes{ 141 constructor(x,y,VelX,VelY,width,height,time){ 142 super(x,y,VelX,VelY,width,height,time); 143 } 144 laneupdate(){ 145 146 } 147 148 lanedisplay(){ 149 150 } 151} 152//判定枠 153const NotesJudge = function(key_send,start,fin){ 154 const judgetime = Math.abs(10-Math.abs(start-fin)); 155 if(key_send.up_push){ 156 if((judgetime <= Judge.PERFECT) && (judgetime >= 0)){ 157 console.log(`Perfect: ${judgetime}ms`); 158 } 159 160 if(judgetime <= Judge.GOOD && judgetime > Judge.PERFECT){ 161 console.log(`GOOD: ${judgetime}ms`); 162 } 163 164 if(judgetime <= Judge.BAD && judgetime > Judge.GOOD){ 165 console.log(`BAD: ${judgetime}ms`); 166 } 167 if(judgetime <= Judge.MISS && judgetime > Judge.BAD){ 168 console.log(`MISS: ${judgetime}ms`); 169 } 170 } 171 172 if(key_send.down_push){ 173 if((judgetime <= Judge.PERFECT) && (judgetime >= 0)){ 174 console.log(`Perfect: ${judgetime}ms`); 175 } 176 177 if(judgetime <= Judge.GOOD && judgetime > Judge.PERFECT){ 178 console.log(`GOOD: ${judgetime}ms`); 179 } 180 181 if(judgetime <= Judge.BAD && judgetime > Judge.GOOD){ 182 console.log(`BAD: ${judgetime}ms`); 183 } 184 if(judgetime <= Judge.MISS && judgetime > Judge.BAD){ 185 console.log(`MISS: ${judgetime}ms`); 186 } 187 } 188 189 if(key_send.left_push){ 190 if((judgetime <= Judge.PERFECT) && (judgetime >= 0)){ 191 console.log(`Perfect: ${judgetime}ms`); 192 } 193 194 if(judgetime <= Judge.GOOD && judgetime > Judge.PERFECT){ 195 console.log(`GOOD: ${judgetime}ms`); 196 } 197 198 if(judgetime <= Judge.BAD && judgetime > Judge.GOOD){ 199 console.log(`BAD: ${judgetime}ms`); 200 } 201 if(judgetime <= Judge.MISS && judgetime > Judge.BAD){ 202 console.log(`MISS: ${judgetime}ms`); 203 } 204 } 205 if(key_send.right_push){ 206 if((judgetime <= Judge.PERFECT) && (judgetime >= 0)){ 207 console.log(`Perfect: ${judgetime}ms`); 208 } 209 210 if(judgetime <= Judge.GOOD && judgetime > Judge.PERFECT){ 211 console.log(`GOOD: ${judgetime}ms`); 212 } 213 214 if(judgetime <= Judge.BAD && judgetime > Judge.GOOD){ 215 console.log(`BAD: ${judgetime}ms`); 216 } 217 if(judgetime <= Judge.MISS && judgetime > Judge.BAD){ 218 console.log(`MISS: ${judgetime}ms`); 219 } 220 } 221}; 222 223document.addEventListener('keydown',(event)=>{ 224 const check = new Pushing(); 225 switch (event.key){ 226 case 'ArrowUp': 227 acction_time.start_time = event.timeStamp / 1000; 228 check.up_push = true; 229 NotesJudge(check.uppush,param.start,param.fin); 230 break; 231 case 'ArrowDown': 232 acction_time.start_time =event.timeStamp / 1000; 233 check.down_push = true; 234 NotesJudge(check.downpush,param.start,param.fin); 235 break; 236 case 'ArrowLeft': 237 acction_time.start_time =event.timeStamp / 1000; 238 check.left_push = true; 239 NotesJudge(check.left,param.start,param.fin); 240 break; 241 case 'ArrowRight': 242 acction_time.start_time =event.timeStamp / 1000; 243 check.right_push = true; 244 NotesJudge(check.right,param.start,param.fin); 245 break; 246 } 247 console.log(`keydown: ${param.start}ms`); 248 249}); 250 251 252document.addEventListener('keyup', (event) => { 253 const check = new Pushing(); 254 switch (event.key){ 255 case 'ArrowUp': 256 check.up_push = false; 257 break; 258 case 'ArrowDown': 259 check.down_push = false; 260 break; 261 case 'ArrowLeft': 262 check.left_push = false; 263 break; 264 case 'ArrowRight': 265 check.right_push = false; 266 break; 267 } 268 //console.log(`keyup: ${param.end}ms`); 269});

試したこと

・performance.now()からevent.timeStampに変更し、処理速度の改善を行った。
・配列notes_LEGENDALYをforeachで制御しようとしたが、あまりにも負荷がかかりすぎていたため断念。

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

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

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

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

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

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

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

guest

回答2

0

仮に1曲に2千個の「ノーツ」があったとしても、一度に画面に出ているのはたぶん数十個ですよね。描画やジャッジに関してはその画面内にあるノーツだけを処理するようにしましょう。

仮に、ノーツのデータが次のように定義されているとして、

js

1const notesData = [ 2 // timeOffset: 曲の先頭からのミリ秒 3 // type: プレーヤがどのボタンを押すべきか 4 {timeOffset; 100, type: '↑'}, 5 {timeOffset; 100, type: '↓'}, 6 {timeOffset; 110, type: '←'}, 7 {timeOffset; 120, type: '→'}, 8 ... (曲の終わりまで何百個〜数千個も続く) 9];
  • visibleNotes という空のリストを用意しておく
  • nextDataIndex = 0 という変数を用意しておく
  • 曲が始まって n ミリ秒経過したら、
    • notesData[nextDataIndex] から nextDataIndex を増やしてループして、timeOffset が n-D 以下の要素から Notes オブジェクトを new して、visibleNotespush() する。timeOffset が n-D より大きくなったらループ終了。D は画面内で事前に表示するミリ秒。
    • visibleNotes に入っている Notes のうち、timeOffset が n より小さいものを削除する
    • visibleNotes に残っている Notes を描画する

投稿2023/07/06 15:40

int32_t

総合スコア21927

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

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

falcon_function

2023/07/09 07:47

ありがとうございます。 アドバイス通りにコードを書こうとしてみましたが、まだ内容を理解しきれていないためか動作いたしません。 お手数ですが、どの部分が間違っているのかご教授いただけますでしょうか。 ※以下のコードはloop関数内に記述されているとします。 if(param.end){ while(nextDataindex < notes_LEGENDALY.length){ if(notes_LEGENDALY[nextDataindex].time <= (param.end - 10)){ let moving = new Notes(notes_LEGENDALY[nextDataindex].x,notes_LEGENDALY[nextDataindex].y,notes_LEGENDALY[nextDataindex].VelX,notes_LEGENDALY[nextDataindex].VelY,notes_LEGENDALY[nextDataindex].width,notes_LEGENDALY[nextDataindex].height,notes_LEGENDALY[nextDataindex].time); visibleNotes.push(moving); nextDataindex++; } else if(notes_LEGENDALY[nextDataindex].time > (param.end - 10)){ break; } if(visiableNotes[nextDataindex].time < param.end){ visibleNotes[nextDataindex].time=null; } } moving.notesdrawing(); moving.notesmoving(); }
int32_t

2023/07/10 00:29

> param.end - 10 これがミリ秒だとすると、10 は小さすぎると思います。10msec って、1/60秒よりも小さいですよ。 > if(visiableNotes[nextDataindex].time < param.end){ この判定はwhileの中に入れてはいけません。while の前(か後)に visibleNotes を先頭からチェックして、splice() で配列要素を削除しましょう。 > moving.notesdrawing(); > moving.notesmoving(); ここだと moving が未定義でエラー出てませんかね。 while の後に、visibleNotes 全体をループしてそれぞれの要素に notedrawing() と notesmoving() を呼ぶ必要があります。
falcon_function

2023/07/10 09:57

アドバイス通り修正してみました。しかし、描画と移動の部分がどう呼び出したらよいのかイメージがわかなかったため、その部分は記述しておりません。 function loop(){ if(param.end){ while(nextDataindex < notes_LEGENDALY.length){ if(notes_LEGENDALY[nextDataindex].time <= Math.abs(param.end - 40000)){ let moving = new Notes(notes_LEGENDALY[nextDataindex].x,notes_LEGENDALY[nextDataindex].y,notes_LEGENDALY[nextDataindex].VelX,notes_LEGENDALY[nextDataindex].VelY,notes_LEGENDALY[nextDataindex].width,notes_LEGENDALY[nextDataindex].height,notes_LEGENDALY[nextDataindex].time); visibleNotes.push(moving); nextDataindex++; } else if(notes_LEGENDALY[nextDataindex].time > Math.abs(param.end - 40000)){ break; } } for(let i = 0; i < visibleNotes.length;i++){ if(visibleNotes[i].time < param.end){ visibleNotes.splice(i,1); } } //ここに描画と移動処理をしたいが、visibleNotes全体をループするの意味が分からなかった } requestAnimationFrame(loop); } requestAnimationFrame(loop);
int32_t

2023/07/10 14:11

たとえば、 for (const n of visibleNotes) { n.notesdrawing(); n.notesmoving(); } こんな感じでしょう。
falcon_function

2023/07/11 14:25

上記のコードも入れて試してみましたが動作しませんでした。しかし、重要なことを忘れていました。 ・notesdrawingメソッド自体が、配列ではないため、最後に読み込まれたデータを基にしか描画しない。 ・param.endは事前に1000で割っているので、ミリ秒ではなく秒単位になる ・Dは固定値なのか、それともクラスNotes内にあるtimeを基にすればよいのかわからなかった この部分が解消されないと実装は難しいとわかりました・・・
int32_t

2023/07/13 08:13

Notesクラスはいろいろとおかしいところがありますね。直し方がわからかったら新たな質問を立ててください。 (一つの質問で複数の問題を解決しようとするとごちゃごちゃしすぎるので)
guest

0

自己解決

複数描画することに成功したのでご報告いたします。動作自体は問題ないですが、少し読み込み中に遅延が発生しているのでそこが問題点でしょう・・・

JavaScript

1for(let i = 0; i < notes_LEGENDALY.length;i++){ 2 notes_LEGENDALY[i].img = new Image(); 3 notes_LEGENDALY[i].img.src = notes_LEGENDALY[i].image; 4 let y = notes_LEGENDALY[i].y 5 notes_LEGENDALY[i].img.addEventListener("load",(event)=>{ 6 //ctx.clearRect(0,0,g_width,g_height); 7 ctx.drawImage(notes_LEGENDALY[i].img,(sprite*220),0,220,220,notes_LEGENDALY[i].x,y,notes_LEGENDALY[i].width,notes_LEGENDALY[i].height); 8 }); 9} 10function loop(){ 11 if(param.end){ 12 while(nextDataindex < (notes_LEGENDALY.length-1)){ 13 if(notes_LEGENDALY[nextDataindex].time <= Math.abs(param.end-10)){ 14 moving = new Notes(notes_LEGENDALY[nextDataindex].x,notes_LEGENDALY[nextDataindex].y,notes_LEGENDALY[nextDataindex].VelX,notes_LEGENDALY[nextDataindex].VelY,notes_LEGENDALY[nextDataindex].width,notes_LEGENDALY[nextDataindex].height,notes_LEGENDALY[nextDataindex].time,notes_LEGENDALY[nextDataindex].image,notes_LEGENDALY[nextDataindex].ways); 15 visibleNotes.push(moving); 16 } else if(notes_LEGENDALY[nextDataindex].time > Math.abs(param.end - 10)){ 17 break; 18 } 19 nextDataindex++; 20 } 21 for(let i = 0;i < visibleNotes.length;i++){ 22 if(visibleNotes[i].time < param.end){ 23 visibleNotes.splice(i,1); 24 } 25 } 26 } 27 ctx.clearRect(0,0,g_width,g_height); 28 for(let i = 0; i < notes_LEGENDALY.length;i++){ 29 notes_LEGENDALY[i].y -= notes_LEGENDALY[i].VelY; 30 if(notes_LEGENDALY[i].y < -notes_LEGENDALY[i].height){ 31 notes_LEGENDALY[i].y = g_height; 32 } 33 34 ctx.drawImage(notes_LEGENDALY[i].img,(sprite*220),0,220,220,notes_LEGENDALY[i].x,notes_LEGENDALY[i].y,notes_LEGENDALY[i].width,notes_LEGENDALY[i].height); 35 } 36 requestAnimationFrame(loop); 37 38 39} 40requestAnimationFrame(loop); 41//判定枠

投稿2023/07/16 11:14

falcon_function

総合スコア10

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.31%

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

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

質問する

関連した質問