summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2024-01-10 11:38:49 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2024-01-10 11:38:49 +0900
commit3d9e42efca8792bcfa1be7bd6125cf732db50fdb (patch)
treec45cf011e869e215b54aa79058d1e7a5ce71f7c1
parentfix(drop-and-fusion): バブルゲームのリトライボタンでリトラ... (diff)
downloadsharkey-3d9e42efca8792bcfa1be7bd6125cf732db50fdb.tar.gz
sharkey-3d9e42efca8792bcfa1be7bd6125cf732db50fdb.tar.bz2
sharkey-3d9e42efca8792bcfa1be7bd6125cf732db50fdb.zip
enhance(drop-and-fusion): リプレイの倍速再生対応
-rw-r--r--packages/frontend/src/pages/drop-and-fusion.vue12
-rw-r--r--packages/frontend/src/scripts/drop-and-fusion-engine.ts79
2 files changed, 52 insertions, 39 deletions
diff --git a/packages/frontend/src/pages/drop-and-fusion.vue b/packages/frontend/src/pages/drop-and-fusion.vue
index d041a675f8..f585519459 100644
--- a/packages/frontend/src/pages/drop-and-fusion.vue
+++ b/packages/frontend/src/pages/drop-and-fusion.vue
@@ -103,7 +103,11 @@ SPDX-License-Identifier: AGPL-3.0-only
<div v-if="replaying" style="display: flex;">
<div :class="$style.frame" style="flex: 1; margin-right: 10px;">
<div :class="$style.frameInner">
- <MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END REPLAY</MkButton>
+ <div class="_buttonsCenter">
+ <MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END REPLAY</MkButton>
+ <MkButton :primary="replayPlaybackRate === 2" @click="replayPlaybackRate = replayPlaybackRate === 2 ? 1 : 2"><i class="ti ti-player-track-next"></i> x2</MkButton>
+ <MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ti ti-player-track-next"></i> x4</MkButton>
+ </div>
</div>
</div>
</div>
@@ -437,10 +441,15 @@ const gameStarted = ref(false);
const highScore = ref<number | null>(null);
const showConfig = ref(false);
const replaying = ref(false);
+const replayPlaybackRate = ref(1);
const mute = ref(false);
const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume);
const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume);
+watch(replayPlaybackRate, (newValue) => {
+ game.replayPlaybackRate = newValue;
+});
+
function onClick(ev: MouseEvent) {
if (!containerElRect) return;
if (replaying.value) return;
@@ -493,6 +502,7 @@ function end() {
game.dispose();
isGameOver.value = false;
replaying.value = false;
+ replayPlaybackRate.value = 1;
currentPick.value = null;
dropReady.value = true;
stock.value = [];
diff --git a/packages/frontend/src/scripts/drop-and-fusion-engine.ts b/packages/frontend/src/scripts/drop-and-fusion-engine.ts
index 16fe87d97a..a59eb271ec 100644
--- a/packages/frontend/src/scripts/drop-and-fusion-engine.ts
+++ b/packages/frontend/src/scripts/drop-and-fusion-engine.ts
@@ -44,7 +44,7 @@ export class DropAndFusionGame extends EventEmitter<{
gameOver: () => void;
}> {
private PHYSICS_QUALITY_FACTOR = 16; // 低いほどパフォーマンスが高いがガタガタして安定しなくなる、逆に高すぎても何故か不安定になる
- private COMBO_INTERVAL = 1000;
+ private COMBO_INTERVAL = 60; // frame
public readonly DROP_INTERVAL = 500;
public readonly PLAYAREA_MARGIN = 25;
private STOCK_MAX = 4;
@@ -76,7 +76,7 @@ export class DropAndFusionGame extends EventEmitter<{
private latestDroppedBodyId: Matter.Body['id'] | null = null;
private latestDroppedAt = 0;
- private latestFusionedAt = 0;
+ private latestFusionedAt = 0; // frame
private stock: { id: string; mono: Mono }[] = [];
private holding: { id: string; mono: Mono } | null = null;
@@ -100,6 +100,8 @@ export class DropAndFusionGame extends EventEmitter<{
private comboIntervalId: number | null = null;
+ public replayPlaybackRate = 1;
+
constructor(opts: {
canvas: HTMLCanvasElement;
width: number;
@@ -219,13 +221,12 @@ export class DropAndFusionGame extends EventEmitter<{
}
private fusion(bodyA: Matter.Body, bodyB: Matter.Body) {
- const now = Date.now();
- if (this.latestFusionedAt > now - this.COMBO_INTERVAL) {
+ if (this.latestFusionedAt > this.frame - this.COMBO_INTERVAL) {
this.combo++;
} else {
this.combo = 1;
}
- this.latestFusionedAt = now;
+ this.latestFusionedAt = this.frame;
// TODO: 単に位置だけでなくそれぞれの動きベクトルも融合する?
const newX = (bodyA.position.x + bodyB.position.x) / 2;
@@ -390,44 +391,43 @@ export class DropAndFusionGame extends EventEmitter<{
}
});
- this.comboIntervalId = window.setInterval(() => {
- if (this.latestFusionedAt < Date.now() - this.COMBO_INTERVAL) {
- this.combo = 0;
- }
- }, 500);
-
if (logs) {
const playTick = () => {
- this.frame++;
- const log = logs.find(x => x.frame === this.frame - 1);
- if (log) {
- switch (log.operation) {
- case 'drop': {
- this.drop(log.x);
- break;
- }
- case 'hold': {
- this.hold();
- break;
- }
- case 'surrender': {
- this.surrender();
- break;
- }
- default:
- break;
+ for (let i = 0; i < this.replayPlaybackRate; i++) {
+ this.frame++;
+ if (this.latestFusionedAt < this.frame - this.COMBO_INTERVAL) {
+ this.combo = 0;
}
- }
- this.tickCallbackQueue = this.tickCallbackQueue.filter(x => {
- if (x.frame === this.frame) {
- x.callback();
- return false;
- } else {
- return true;
+ const log = logs.find(x => x.frame === this.frame - 1);
+ if (log) {
+ switch (log.operation) {
+ case 'drop': {
+ this.drop(log.x);
+ break;
+ }
+ case 'hold': {
+ this.hold();
+ break;
+ }
+ case 'surrender': {
+ this.surrender();
+ break;
+ }
+ default:
+ break;
+ }
}
- });
+ this.tickCallbackQueue = this.tickCallbackQueue.filter(x => {
+ if (x.frame === this.frame) {
+ x.callback();
+ return false;
+ } else {
+ return true;
+ }
+ });
- Matter.Engine.update(this.engine, this.TICK_DELTA);
+ Matter.Engine.update(this.engine, this.TICK_DELTA);
+ }
if (!this.isGameOver) {
this.tickRaf = window.requestAnimationFrame(playTick);
@@ -446,6 +446,9 @@ export class DropAndFusionGame extends EventEmitter<{
private tick() {
this.frame++;
+ if (this.latestFusionedAt < this.frame - this.COMBO_INTERVAL) {
+ this.combo = 0;
+ }
this.tickCallbackQueue = this.tickCallbackQueue.filter(x => {
if (x.frame === this.frame) {
x.callback();