Replace game audio with Pixabay tracks

This commit is contained in:
2026-06-22 12:22:09 +09:00
parent 381b2cf14c
commit 89f2251d0b
21 changed files with 112 additions and 7 deletions

26
docs/audio-sources.md Normal file
View File

@@ -0,0 +1,26 @@
# Audio Sources
Downloaded from Pixabay on 2026-06-22 and used under the Pixabay Content License.
License reference: https://pixabay.com/service/license-summary/
## Background Music
| Project file | Pixabay title | Creator | Source |
| --- | --- | --- | --- |
| `src/assets/audio/bgm/title-theme.mp3` | Chinese ancient music, World, Ethnic music | ET11LX | https://pixabay.com/music/china-chinese-ancient-style-music-love-et11lx-%E5%8F%A4%E9%A3%8E-%E6%8A%92%E6%83%85-%E8%AF%86-155926/ |
| `src/assets/audio/bgm/story-dark.mp3` | Chinese, Asia, China music | Reklamistrz | https://pixabay.com/music/meditationspiritual-ancient-chinese-dali-448882/ |
| `src/assets/audio/bgm/oath-theme.mp3` | Chinese ancient style, Ancient style, World music | ET11LX | https://pixabay.com/music/world-chinese-ancient-style-music-love-et%E5%8D%81%E4%B8%80lx-%E5%8F%A4%E9%A3%8E-%E6%8A%92%E6%83%85-%E8%AF%86%E4%B8%B0%E5%AF%8C%E9%85%8D%E5%99%A8%E6%8E%A8%E9%AB%98%E6%BD%AE%E7%89%88-247345/ |
| `src/assets/audio/bgm/militia-theme.mp3` | Orchestra, Heroic, Warrior music | NR-Music | https://pixabay.com/music/adventure-short-heroic-orchestral-loop-541095/ |
| `src/assets/audio/bgm/battle-prep.mp3` | Epic, Battle, Knight music | DeusLower | https://pixabay.com/music/mystery-epic-battle-orchestra-music-241006/ |
## Sound Effects
| Project file | Pixabay title | Creator | Source |
| --- | --- | --- | --- |
| `src/assets/audio/sfx/ui-select.mp3` | Ui, Click, Retro sound effect | SoundShelfStudio | https://pixabay.com/sound-effects/film-special-effects-ui-click-retro-514601/ |
| `src/assets/audio/sfx/sword-slash.mp3` | Sword, Slash, Swing sound effect | DavidDumaisAudio | https://pixabay.com/sound-effects/film-special-effects-sword-slash-and-swing-185432/ |
| `src/assets/audio/sfx/combat-impact.mp3` | Sword, Slash, Weapon sound effect | DavidDumaisAudio | https://pixabay.com/sound-effects/film-special-effects-sword-slash-with-metal-shield-impact-185433/ |
| `src/assets/audio/sfx/strategy-cast.mp3` | Designed, Magic spell, Projectile sound effect | RescopicSound | https://pixabay.com/sound-effects/film-special-effects-elemental-magic-spell-impact-outgoing-228342/ |
| `src/assets/audio/sfx/cart-roll.mp3` | Cart, Wheel, Cobblestones sound effect | Fronbondi_Skegs | https://pixabay.com/sound-effects/film-special-effects-foley-heavily-laden-cart-moving-over-cobblestones-sound-effect-246657/ |
| `src/assets/audio/sfx/exp-gain.mp3` | Level up, Level up sound, Game level up sound effect | Universfield | https://pixabay.com/sound-effects/film-special-effects-level-up-06-370051/ |

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,21 +1,33 @@
type AudioTrackMap = Record<string, string>;
type PlayEffectOptions = {
volume?: number;
rate?: number;
stopAfterMs?: number;
};
class SoundDirector {
private context?: AudioContext;
private master?: GainNode;
private started = false;
private muted = false;
private musicTracks: AudioTrackMap = {};
private effectTracks: AudioTrackMap = {};
private music?: HTMLAudioElement;
private currentMusicKey?: string;
private pendingMusicKey?: string;
private musicFadeTimer?: number;
private readonly musicVolume = 0.14;
private readonly musicVolume = 0.22;
private readonly effectVolume = 0.42;
registerMusicTracks(tracks: AudioTrackMap) {
this.musicTracks = tracks;
}
registerEffectTracks(tracks: AudioTrackMap) {
this.effectTracks = tracks;
}
start() {
if (this.started) {
this.resume();
@@ -70,10 +82,48 @@ class SoundDirector {
}
playSelect() {
if (this.playEffect('ui-select', { volume: 0.24, stopAfterMs: 420 })) {
return;
}
this.playTone(300, 0.07, 0.04, 'sine');
window.setTimeout(() => this.playTone(450, 0.08, 0.028, 'sine'), 70);
}
playEffect(key: string, options: PlayEffectOptions = {}) {
if (!this.started || this.muted) {
return false;
}
const src = this.effectTracks[key];
if (!src) {
return false;
}
const effect = new Audio(src);
effect.preload = 'auto';
effect.volume = options.volume ?? this.effectVolume;
effect.playbackRate = options.rate ?? 1;
effect.muted = this.muted;
effect.addEventListener(
'ended',
() => {
effect.src = '';
},
{ once: true }
);
if (options.stopAfterMs) {
window.setTimeout(() => {
effect.pause();
effect.src = '';
}, options.stopAfterMs);
}
void effect.play().catch(() => undefined);
return true;
}
playMusic(key?: string) {
if (!key) {
return;

View File

@@ -1,8 +1,14 @@
import battlePrepUrl from '../../assets/audio/bgm/battle-prep.wav';
import militiaThemeUrl from '../../assets/audio/bgm/militia-theme.wav';
import oathThemeUrl from '../../assets/audio/bgm/oath-theme.wav';
import storyDarkUrl from '../../assets/audio/bgm/story-dark.wav';
import titleThemeUrl from '../../assets/audio/bgm/title-theme.wav';
import battlePrepUrl from '../../assets/audio/bgm/battle-prep.mp3';
import militiaThemeUrl from '../../assets/audio/bgm/militia-theme.mp3';
import oathThemeUrl from '../../assets/audio/bgm/oath-theme.mp3';
import storyDarkUrl from '../../assets/audio/bgm/story-dark.mp3';
import titleThemeUrl from '../../assets/audio/bgm/title-theme.mp3';
import cartRollUrl from '../../assets/audio/sfx/cart-roll.mp3';
import combatImpactUrl from '../../assets/audio/sfx/combat-impact.mp3';
import expGainUrl from '../../assets/audio/sfx/exp-gain.mp3';
import strategyCastUrl from '../../assets/audio/sfx/strategy-cast.mp3';
import swordSlashUrl from '../../assets/audio/sfx/sword-slash.mp3';
import uiSelectUrl from '../../assets/audio/sfx/ui-select.mp3';
export const musicTracks = {
'title-theme': titleThemeUrl,
@@ -12,4 +18,14 @@ export const musicTracks = {
'battle-prep': battlePrepUrl
} as const;
export const effectTracks = {
'ui-select': uiSelectUrl,
'sword-slash': swordSlashUrl,
'combat-impact': combatImpactUrl,
'strategy-cast': strategyCastUrl,
'cart-roll': cartRollUrl,
'exp-gain': expGainUrl
} as const;
export type MusicTrackKey = keyof typeof musicTracks;
export type EffectTrackKey = keyof typeof effectTracks;

View File

@@ -1270,6 +1270,10 @@ export class BattleScene extends Phaser.Scene {
await this.animateCombatGauge(defenderStatus.hpFill, result.previousDefenderHp / result.defender.maxHp, result.defender.hp / result.defender.maxHp, 420);
defenderStatus.hpText.setText(`${result.defender.hp} / ${result.defender.maxHp}`);
if (result.characterGrowth.amount > 0) {
soundDirector.playEffect('exp-gain', { volume: result.characterGrowth.leveled ? 0.44 : 0.28, stopAfterMs: 1000 });
}
await this.animateCombatGauge(
attackerStatus.expFill,
result.characterGrowth.previousExp / result.characterGrowth.next,
@@ -1400,6 +1404,7 @@ export class BattleScene extends Phaser.Scene {
) {
const direction = result.attacker.faction === 'ally' ? 1 : -1;
if (result.action === 'attack') {
soundDirector.playEffect('sword-slash', { volume: 0.52, stopAfterMs: 700 });
this.tweens.add({
targets: attackerSprite,
x: attackerX + direction * 84,
@@ -1418,6 +1423,11 @@ export class BattleScene extends Phaser.Scene {
return;
}
soundDirector.playEffect(result.action === 'item' ? 'cart-roll' : 'strategy-cast', {
volume: result.action === 'item' ? 0.34 : 0.44,
stopAfterMs: result.action === 'item' ? 720 : 950
});
const projectile =
result.action === 'item'
? this.createCombatCart(attackerX + direction * 78, groundY - 10, depth)
@@ -1462,6 +1472,8 @@ export class BattleScene extends Phaser.Scene {
}
private showCombatImpact(result: CombatResult, x: number, y: number, depth: number) {
soundDirector.playEffect('combat-impact', { volume: result.action === 'strategy' ? 0.3 : 0.48, stopAfterMs: 650 });
const impact = this.trackCombatObject(this.add.star(x, y, 8, 12, 42, result.action === 'strategy' ? 0x6cc5ff : 0xffdf7b, 0.92));
impact.setDepth(depth);
this.tweens.add({

View File

@@ -1,5 +1,5 @@
import Phaser from 'phaser';
import { musicTracks } from './game/audio/audioAssets';
import { effectTracks, musicTracks } from './game/audio/audioAssets';
import { soundDirector } from './game/audio/SoundDirector';
import { BattleScene } from './game/scenes/BattleScene';
import { BootScene } from './game/scenes/BootScene';
@@ -42,6 +42,7 @@ const config: Phaser.Types.Core.GameConfig = {
};
soundDirector.registerMusicTracks(musicTracks);
soundDirector.registerEffectTracks(effectTracks);
const game = new Phaser.Game(config);