mirror of
				https://codeberg.org/yeentown/barkey.git
				synced 2025-11-04 07:24:13 +00:00 
			
		
		
		
	enhance(drop-and-fusion): リプレイの倍速再生対応
This commit is contained in:
		
							parent
							
								
									138a248a6c
								
							
						
					
					
						commit
						3d9e42efca
					
				
					 2 changed files with 52 additions and 39 deletions
				
			
		| 
						 | 
					@ -103,7 +103,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 | 
				
			||||||
			<div v-if="replaying" style="display: flex;">
 | 
								<div v-if="replaying" style="display: flex;">
 | 
				
			||||||
				<div :class="$style.frame" style="flex: 1; margin-right: 10px;">
 | 
									<div :class="$style.frame" style="flex: 1; margin-right: 10px;">
 | 
				
			||||||
					<div :class="$style.frameInner">
 | 
										<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>
 | 
									</div>
 | 
				
			||||||
			</div>
 | 
								</div>
 | 
				
			||||||
| 
						 | 
					@ -437,10 +441,15 @@ const gameStarted = ref(false);
 | 
				
			||||||
const highScore = ref<number | null>(null);
 | 
					const highScore = ref<number | null>(null);
 | 
				
			||||||
const showConfig = ref(false);
 | 
					const showConfig = ref(false);
 | 
				
			||||||
const replaying = ref(false);
 | 
					const replaying = ref(false);
 | 
				
			||||||
 | 
					const replayPlaybackRate = ref(1);
 | 
				
			||||||
const mute = ref(false);
 | 
					const mute = ref(false);
 | 
				
			||||||
const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume);
 | 
					const bgmVolume = ref(defaultStore.state.dropAndFusion.bgmVolume);
 | 
				
			||||||
const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume);
 | 
					const sfxVolume = ref(defaultStore.state.dropAndFusion.sfxVolume);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(replayPlaybackRate, (newValue) => {
 | 
				
			||||||
 | 
						game.replayPlaybackRate = newValue;
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onClick(ev: MouseEvent) {
 | 
					function onClick(ev: MouseEvent) {
 | 
				
			||||||
	if (!containerElRect) return;
 | 
						if (!containerElRect) return;
 | 
				
			||||||
	if (replaying.value) return;
 | 
						if (replaying.value) return;
 | 
				
			||||||
| 
						 | 
					@ -493,6 +502,7 @@ function end() {
 | 
				
			||||||
	game.dispose();
 | 
						game.dispose();
 | 
				
			||||||
	isGameOver.value = false;
 | 
						isGameOver.value = false;
 | 
				
			||||||
	replaying.value = false;
 | 
						replaying.value = false;
 | 
				
			||||||
 | 
						replayPlaybackRate.value = 1;
 | 
				
			||||||
	currentPick.value = null;
 | 
						currentPick.value = null;
 | 
				
			||||||
	dropReady.value = true;
 | 
						dropReady.value = true;
 | 
				
			||||||
	stock.value = [];
 | 
						stock.value = [];
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,7 +44,7 @@ export class DropAndFusionGame extends EventEmitter<{
 | 
				
			||||||
	gameOver: () => void;
 | 
						gameOver: () => void;
 | 
				
			||||||
}> {
 | 
					}> {
 | 
				
			||||||
	private PHYSICS_QUALITY_FACTOR = 16; // 低いほどパフォーマンスが高いがガタガタして安定しなくなる、逆に高すぎても何故か不安定になる
 | 
						private PHYSICS_QUALITY_FACTOR = 16; // 低いほどパフォーマンスが高いがガタガタして安定しなくなる、逆に高すぎても何故か不安定になる
 | 
				
			||||||
	private COMBO_INTERVAL = 1000;
 | 
						private COMBO_INTERVAL = 60; // frame
 | 
				
			||||||
	public readonly DROP_INTERVAL = 500;
 | 
						public readonly DROP_INTERVAL = 500;
 | 
				
			||||||
	public readonly PLAYAREA_MARGIN = 25;
 | 
						public readonly PLAYAREA_MARGIN = 25;
 | 
				
			||||||
	private STOCK_MAX = 4;
 | 
						private STOCK_MAX = 4;
 | 
				
			||||||
| 
						 | 
					@ -76,7 +76,7 @@ export class DropAndFusionGame extends EventEmitter<{
 | 
				
			||||||
	private latestDroppedBodyId: Matter.Body['id'] | null = null;
 | 
						private latestDroppedBodyId: Matter.Body['id'] | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private latestDroppedAt = 0;
 | 
						private latestDroppedAt = 0;
 | 
				
			||||||
	private latestFusionedAt = 0;
 | 
						private latestFusionedAt = 0; // frame
 | 
				
			||||||
	private stock: { id: string; mono: Mono }[] = [];
 | 
						private stock: { id: string; mono: Mono }[] = [];
 | 
				
			||||||
	private holding: { id: string; mono: Mono } | null = null;
 | 
						private holding: { id: string; mono: Mono } | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -100,6 +100,8 @@ export class DropAndFusionGame extends EventEmitter<{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private comboIntervalId: number | null = null;
 | 
						private comboIntervalId: number | null = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						public replayPlaybackRate = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	constructor(opts: {
 | 
						constructor(opts: {
 | 
				
			||||||
		canvas: HTMLCanvasElement;
 | 
							canvas: HTMLCanvasElement;
 | 
				
			||||||
		width: number;
 | 
							width: number;
 | 
				
			||||||
| 
						 | 
					@ -219,13 +221,12 @@ export class DropAndFusionGame extends EventEmitter<{
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private fusion(bodyA: Matter.Body, bodyB: Matter.Body) {
 | 
						private fusion(bodyA: Matter.Body, bodyB: Matter.Body) {
 | 
				
			||||||
		const now = Date.now();
 | 
							if (this.latestFusionedAt > this.frame - this.COMBO_INTERVAL) {
 | 
				
			||||||
		if (this.latestFusionedAt > now - this.COMBO_INTERVAL) {
 | 
					 | 
				
			||||||
			this.combo++;
 | 
								this.combo++;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			this.combo = 1;
 | 
								this.combo = 1;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		this.latestFusionedAt = now;
 | 
							this.latestFusionedAt = this.frame;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// TODO: 単に位置だけでなくそれぞれの動きベクトルも融合する?
 | 
							// TODO: 単に位置だけでなくそれぞれの動きベクトルも融合する?
 | 
				
			||||||
		const newX = (bodyA.position.x + bodyB.position.x) / 2;
 | 
							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) {
 | 
							if (logs) {
 | 
				
			||||||
			const playTick = () => {
 | 
								const playTick = () => {
 | 
				
			||||||
				this.frame++;
 | 
									for (let i = 0; i < this.replayPlaybackRate; i++) {
 | 
				
			||||||
				const log = logs.find(x => x.frame === this.frame - 1);
 | 
										this.frame++;
 | 
				
			||||||
				if (log) {
 | 
										if (this.latestFusionedAt < this.frame - this.COMBO_INTERVAL) {
 | 
				
			||||||
					switch (log.operation) {
 | 
											this.combo = 0;
 | 
				
			||||||
						case 'drop': {
 | 
					 | 
				
			||||||
							this.drop(log.x);
 | 
					 | 
				
			||||||
							break;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						case 'hold': {
 | 
					 | 
				
			||||||
							this.hold();
 | 
					 | 
				
			||||||
							break;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						case 'surrender': {
 | 
					 | 
				
			||||||
							this.surrender();
 | 
					 | 
				
			||||||
							break;
 | 
					 | 
				
			||||||
						}
 | 
					 | 
				
			||||||
						default:
 | 
					 | 
				
			||||||
							break;
 | 
					 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
										const log = logs.find(x => x.frame === this.frame - 1);
 | 
				
			||||||
				this.tickCallbackQueue = this.tickCallbackQueue.filter(x => {
 | 
										if (log) {
 | 
				
			||||||
					if (x.frame === this.frame) {
 | 
											switch (log.operation) {
 | 
				
			||||||
						x.callback();
 | 
												case 'drop': {
 | 
				
			||||||
						return false;
 | 
													this.drop(log.x);
 | 
				
			||||||
					} else {
 | 
													break;
 | 
				
			||||||
						return true;
 | 
												}
 | 
				
			||||||
 | 
												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) {
 | 
									if (!this.isGameOver) {
 | 
				
			||||||
					this.tickRaf = window.requestAnimationFrame(playTick);
 | 
										this.tickRaf = window.requestAnimationFrame(playTick);
 | 
				
			||||||
| 
						 | 
					@ -446,6 +446,9 @@ export class DropAndFusionGame extends EventEmitter<{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private tick() {
 | 
						private tick() {
 | 
				
			||||||
		this.frame++;
 | 
							this.frame++;
 | 
				
			||||||
 | 
							if (this.latestFusionedAt < this.frame - this.COMBO_INTERVAL) {
 | 
				
			||||||
 | 
								this.combo = 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		this.tickCallbackQueue = this.tickCallbackQueue.filter(x => {
 | 
							this.tickCallbackQueue = this.tickCallbackQueue.filter(x => {
 | 
				
			||||||
			if (x.frame === this.frame) {
 | 
								if (x.frame === this.frame) {
 | 
				
			||||||
				x.callback();
 | 
									x.callback();
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue