著者:増田 嶺
前回に引き続き、SLPで開発したリズムゲームを紹介します。前回はゲームで使用する譜面作成ツールについて解説しました。今回は、ゲーム本体について解説します。ゲーム本体は「Visual Studio Code」(VSCode)で開発します。
図5 「js/game/singleNote.js」ファイルに記述するコード
class SingleNote {
constructor(speed, reachTime) {
this.height = note.height;
this.width = note.width;
this.frameColor = 'rgb(' + note.frameColor + ')'; // 枠の色
this.bodyColor = 'rgba(' + note.bodyColor + ')'; // 中の色
this.this.centerY = -this.height; // ノーツ中心のY座標
this.speed = speed * note.speedRatio; // px/ms
// ゲーム開始から判定ラインに到達するまでの時間
this.reachTime = reachTime + note.delay;
// canvasに入る時間
this.appearTime = this.reachTime -
(JUDGE_LINE.centerY + this.height / 2) / this.speed;
// canvasから出る時間
this.hideTime = this.reachTime +
(CANVAS_H - JUDGE_LINE.centerY + this.height / 2) /
this.speed;
this.act = true; // 自身がヒット処理対象かどうか
this.show = false; // 自身がcanvasに描画されるかどうか
}
}
図6 「js/game/backLane.js」ファイルに追加するコード
(略)
generateNote(data) {
this.note = data.map((val) => new SingleNote(val[2], val[3]));
}
(略)
図7 「js/game/singleNote.js」ファイルに追加するコード
class SingleNote {
constructor(speed, reachTime) {
(略)
}
draw(x) {
if (this.show) {
this.centerY = JUDGE_LINE.centerY -
(this.reachTime - time.elapsed) * this.speed;
CTX.fillStyle = this.bodyColor;
CTX.fillRect(x, this.centerY - this.height / 2,
this.width, this.height);
CTX.strokeStyle = this.frameColor;
CTX.strokeRect(
x + LINE_WIDTH / 2,
this.centerY - this.height / 2 + LINE_WIDTH / 2,
this.width - LINE_WIDTH,
this.height - LINE_WIDTH
);
}
}
}
図8 「js/game/backLane.js」ファイルに追加するコード
(略)
drawNote() {
this.note.forEach(val => val.draw(this.x));
}
(略)
図9 「js/game/singleNote.js」ファイルに追加するコード
class SingleNote {
constructor(speed, reachTime) {
(略)
}
draw(x) {
(略)
}
update() {
this.updateShow();
}
updateShow() {
if (this.act || this.show) {
if (this.appearTime <= time.elapsed &&
time.elapsed <= this.hideTime) {
this.show = true;
} else {
this.show = false;
}
}
}
}
図11 「js/game/backLane.js」ファイルに追加するコード
(略)
update() {
this.note.forEach(val => val.update());
}
(略)
図13 「js/game/backLane.js」ファイルに追加するコード
(略)
judge() {
calcElapsedTime(); // 経過時間を更新
// ヒットしているノーツを抽出
const TARGET = this.note.filter(val => val.checkHit(note.hitRange[3]));
// TARGETの先頭から処理、先頭ノーツのグレードがBadだった場合のみ
// 二つ目以降のノーツを処理し、それらのノーツがBadだった場合は中断
const GRADE = [3]
for (let i = 0; TARGET[i] && GRADE[0] === 3; i++) {
GRADE[i] = TARGET[i].getGrade();
// 二つ目以降のノーツがBadの場合はそこで中断
if (i > 0 && GRADE[i] === 3) {
break;
}
JUDGE_LINE.setGrade(GRADE[i]); // ノーツヒットのグレードを描画
TARGET[i].close(); // 判定済みのノーツ処理を停止
}
}
(略)
図15 「js/game/singleNote.js」ファイルに追加するコード
class SingleNote {
(略)
updateShow() {
(略)
}
checkHit(range) {
if (this.act &&
Math.abs(time.elapsed - this.reachTime) <= range) {
return true;
} else {
return false;
}
}
getGrade() {
let grade = 3;
for (let i = 2; i >= 0; i--) {
if (this.checkHit(note.hitRange[i])) {
grade = i;
}
}
return grade;
}
}
図16 「js/game/singleNote.js」ファイルに追加するコード
class SingleNote {
(略)
update() {
this.updateShow();
if (!this.act) {
return;
}
// 判定ラインから自身の判定ゾーンが
// 過ぎた時点で処理
if (this.reachTime < time.elapsed &&
!this.checkHit(note.hitRange[3])) {
JUDGE_LINE.setGrade(3);
this.act = false;
}
}
(略)
close() {
this.act = false;
this.show = false;
}
}
図18 「js/game/gameScoreManager.js」ファイルに追加するコード
(略)
calcScore(grade) {
switch (grade) {
case 0:
this.point += 100 + this.combo;
this.perfect++;
this.combo++;
break;
case 1:
this.point += 80 + this.combo * 0.8;
this.great++;
this.combo++;
break;
case 2:
this.point += 50 + this.combo * 0.5;
this.good++;
this.combo++;
break;
case 3:
this.bad++;
this.combo = 0;
break;
}
if (this.maxCombo < this.combo) {
this.maxCombo = this.combo;
}
}
(略)
図19 「js/game/backLane.js」ファイルに追加するコード
(略)
judge() {
(略)
if (GRADE[i] < 3) {
// Bad以外の判定ならヒットSEを鳴らす
sound.playSE(sound.seList[0]);
} else {
sound.playSE(sound.seList[1]); // Bad判定ならバッドSEを鳴らす
}
gameScore.calcScore(GRADE[i]); // スコアを更新
JUDGE_LINE.setGrade(GRADE[i]); // ノーツヒットのグレードを描画
TARGET[i].close(); // 判定済みのノーツ処理を停止
}
}
(略)
図20 「js/game/singleNote.js」ファイルに追加するコード
class SingleNote {
(略)
update() {
(略)
if (this.reachTime < time.elapsed &&
!this.checkHit(note.hitRange[3])) {
gameScore.calcScore(3);
JUDGE_LINE.setGrade(3);
this.act = false;
}
}
(略)
}
図22 「js/game/backLane.js」ファイルに追加するコード
(略)
draw() {
(略)
if (inputKey.status[this.key]) {
const GRAD = CTX.createLinearGradient(this.x,
JUDGE_LINE.centerY, this.x, CANVAS_H / 3);
GRAD.addColorStop(0.0, 'rgba(' + this.actColor + ', 0.6)');
GRAD.addColorStop(1.0, 'rgba(' + this.actColor + ', 0)');
CTX.fillStyle = GRAD;
CTX.fillRect(this.x, 0, this.width, JUDGE_LINE.centerY);
}
}
(略)