summaryrefslogtreecommitdiff
path: root/packages/frontend/src/scripts
diff options
context:
space:
mode:
authorsyuilo <Syuilotan@yahoo.co.jp>2024-01-08 12:46:20 +0900
committersyuilo <Syuilotan@yahoo.co.jp>2024-01-08 12:46:20 +0900
commite9c3fe12281b97e0d69e106be6de78f04c7b7a4e (patch)
tree75f03b377cbb54f2ec934bcce3c4badc96dc1349 /packages/frontend/src/scripts
parentrefactor(frontend): extract game engine from vue component (diff)
downloadsharkey-e9c3fe12281b97e0d69e106be6de78f04c7b7a4e.tar.gz
sharkey-e9c3fe12281b97e0d69e106be6de78f04c7b7a4e.tar.bz2
sharkey-e9c3fe12281b97e0d69e106be6de78f04c7b7a4e.zip
enhance(frontend): add game bgm and refactor sound system
Diffstat (limited to 'packages/frontend/src/scripts')
-rw-r--r--packages/frontend/src/scripts/drop-and-fusion-engine.ts14
-rw-r--r--packages/frontend/src/scripts/sound.ts75
2 files changed, 33 insertions, 56 deletions
diff --git a/packages/frontend/src/scripts/drop-and-fusion-engine.ts b/packages/frontend/src/scripts/drop-and-fusion-engine.ts
index 7241525a38..b6e735ddf2 100644
--- a/packages/frontend/src/scripts/drop-and-fusion-engine.ts
+++ b/packages/frontend/src/scripts/drop-and-fusion-engine.ts
@@ -199,7 +199,7 @@ export class DropAndFusionGame extends EventEmitter<{
}
this.latestFusionedAt = now;
- // TODO: 単に位置だけでなくそれぞれの動きベクトルも融合する
+ // TODO: 単に位置だけでなくそれぞれの動きベクトルも融合する?
const newX = (bodyA.position.x + bodyB.position.x) / 2;
const newY = (bodyA.position.y + bodyB.position.y) / 2;
@@ -222,8 +222,9 @@ export class DropAndFusionGame extends EventEmitter<{
const additionalScore = Math.round(currentMono.score * comboBonus);
this.score += additionalScore;
+ // TODO: 効果音再生はコンポーネント側の責務なので移動する
const pan = ((newX / this.gameWidth) - 0.5) * 2;
- sound.playRaw('syuilo/bubble2', 1, pan, nextMono.sfxPitch);
+ sound.playUrl('/client-assets/drop-and-fusion/bubble2.mp3', 1, pan, nextMono.sfxPitch);
this.emit('monoAdded', nextMono);
this.emit('fusioned', newX, newY, additionalScore);
@@ -234,7 +235,7 @@ export class DropAndFusionGame extends EventEmitter<{
// Matter.Composite.add(world, body);
// bodies.push(body);
//}
- //sound.playRaw({
+ //sound.playUrl({
// type: 'syuilo/bubble2',
// volume: 1,
//});
@@ -321,10 +322,11 @@ export class DropAndFusionGame extends EventEmitter<{
} else {
const energy = pairs.collision.depth;
if (energy > minCollisionEnergyForSound) {
+ // TODO: 効果音再生はコンポーネント側の責務なので移動する
const vol = (Math.min(maxCollisionEnergyForSound, energy - minCollisionEnergyForSound) / maxCollisionEnergyForSound) / 4;
const pan = ((((bodyA.position.x + bodyB.position.x) / 2) / this.gameWidth) - 0.5) * 2;
const pitch = soundPitchMin + ((soundPitchMax - soundPitchMin) * (1 - (Math.min(10, energy) / 10)));
- sound.playRaw('syuilo/poi1', vol, pan, pitch);
+ sound.playUrl('/client-assets/drop-and-fusion/poi1.mp3', vol, pan, pitch);
}
}
}
@@ -382,8 +384,10 @@ export class DropAndFusionGame extends EventEmitter<{
this.latestDroppedAt = Date.now();
this.emit('dropped');
this.emit('monoAdded', st.mono);
+
+ // TODO: 効果音再生はコンポーネント側の責務なので移動する
const pan = ((x / this.gameWidth) - 0.5) * 2;
- sound.playRaw('syuilo/poi2', 1, pan);
+ sound.playUrl('/client-assets/drop-and-fusion/poi2.mp3', 1, pan);
}
public dispose() {
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index acde78f5fd..690c342c85 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -5,7 +5,6 @@
import type { SoundStore } from '@/store.js';
import { defaultStore } from '@/store.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
let ctx: AudioContext;
const cache = new Map<string, AudioBuffer>();
@@ -89,69 +88,35 @@ export type OperationType = typeof operationTypes[number];
/**
* 音声を読み込む
- * @param soundStore サウンド設定
+ * @param url url
* @param options `useCache`: デフォルトは`true` 一度再生した音声はキャッシュする
*/
-export async function loadAudio(soundStore: {
- type: Exclude<SoundType, '_driveFile_'>;
-} | {
- type: '_driveFile_';
- fileId: string;
- fileUrl: string;
-}, options?: { useCache?: boolean; }) {
+export async function loadAudio(url: string, options?: { useCache?: boolean; }) {
if (_DEV_) console.log('loading audio. opts:', options);
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
- if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
- return;
- }
- // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
if (ctx == null) {
ctx = new AudioContext();
}
if (options?.useCache ?? true) {
- if (soundStore.type === '_driveFile_' && cache.has(soundStore.fileId)) {
- if (_DEV_) console.log('use cache');
- return cache.get(soundStore.fileId) as AudioBuffer;
- } else if (cache.has(soundStore.type)) {
+ if (cache.has(url)) {
if (_DEV_) console.log('use cache');
- return cache.get(soundStore.type) as AudioBuffer;
+ return cache.get(url) as AudioBuffer;
}
}
let response: Response;
- if (soundStore.type === '_driveFile_') {
- try {
- response = await fetch(soundStore.fileUrl);
- } catch (err) {
- try {
- // URLが変わっている可能性があるのでドライブ側からURLを取得するフォールバック
- const apiRes = await misskeyApi('drive/files/show', {
- fileId: soundStore.fileId,
- });
- response = await fetch(apiRes.url);
- } catch (fbErr) {
- // それでも無理なら諦める
- return;
- }
- }
- } else {
- try {
- response = await fetch(`/client-assets/sounds/${soundStore.type}.mp3`);
- } catch (err) {
- return;
- }
+ try {
+ response = await fetch(url);
+ } catch (err) {
+ return;
}
const arrayBuffer = await response.arrayBuffer();
const audioBuffer = await ctx.decodeAudioData(arrayBuffer);
if (options?.useCache ?? true) {
- if (soundStore.type === '_driveFile_') {
- cache.set(soundStore.fileId, audioBuffer);
- } else {
- cache.set(soundStore.type, audioBuffer);
- }
+ cache.set(url, audioBuffer);
}
return audioBuffer;
@@ -180,18 +145,26 @@ export function play(operationType: OperationType) {
* @param soundStore サウンド設定
*/
export async function playFile(soundStore: SoundStore) {
- const buffer = await loadAudio(soundStore);
+ if (soundStore.type === null || (soundStore.type === '_driveFile_' && !soundStore.fileUrl)) {
+ return;
+ }
+ const url = soundStore.type === '_driveFile_' ? soundStore.fileUrl : `/client-assets/sounds/${soundStore.type}.mp3`;
+ const buffer = await loadAudio(url);
if (!buffer) return;
- createSourceNode(buffer, soundStore.volume)?.start();
+ createSourceNode(buffer, soundStore.volume)?.soundSource.start();
}
-export async function playRaw(type: Exclude<SoundType, '_driveFile_'>, volume = 1, pan = 0, playbackRate = 1) {
- const buffer = await loadAudio({ type });
+export async function playUrl(url: string, volume = 1, pan = 0, playbackRate = 1) {
+ const buffer = await loadAudio(url);
if (!buffer) return;
- createSourceNode(buffer, volume, pan, playbackRate)?.start();
+ createSourceNode(buffer, volume, pan, playbackRate)?.soundSource.start();
}
-export function createSourceNode(buffer: AudioBuffer, volume: number, pan = 0, playbackRate = 1) : AudioBufferSourceNode | null {
+export function createSourceNode(buffer: AudioBuffer, volume: number, pan = 0, playbackRate = 1): {
+ soundSource: AudioBufferSourceNode;
+ panNode: StereoPannerNode;
+ gainNode: GainNode;
+} | null {
const masterVolume = defaultStore.state.sound_masterVolume;
if (isMute() || masterVolume === 0 || volume === 0) {
return null;
@@ -211,7 +184,7 @@ export function createSourceNode(buffer: AudioBuffer, volume: number, pan = 0, p
.connect(gainNode)
.connect(ctx.destination);
- return soundSource;
+ return { soundSource, panNode, gainNode };
}
/**