オセロ開発解説 - アニメーションと音

アニメーションと音

コマの3D回転アニメーション、キラキラエフェクト、Web Audio APIによるサイン波/三角波でゲームに臨場感を追加。パフォーマンスとユーザー体験を両立します。

設計思想

アニメーションはCanvasの`requestAnimationFrame`代替として`setInterval`(60fps)を使用し、コマの反転を10フレームで完了。3D効果は`ctx.scale`でY軸圧縮、キラキラは放射グラデーションで表現。音はWeb Audio APIでサイン波(440Hz, 880Hz)、三角波(300Hz)、ノコギリ波/矩形波(結果表示)を生成。音量は0.08で控えめ、ロック機構で重複防止。

サンプルコード


function animateFlip(player) {
    animationFrame = 0;
    const interval = setInterval(() => {
        animationFrame++;
        drawBoard();
        for (const piece of flippingPieces) {
            const progress = Math.min(animationFrame / 10, 1);
            piece.angle = progress * Math.PI;
            piece.scale = 1 + 0.15 * Math.sin(progress * Math.PI);
            ctx.save();
            ctx.translate((piece.c + 0.5) * cellSize, (piece.r + 0.5) * cellSize);
            ctx.scale(1, Math.cos(piece.angle));
            ctx.rotate(piece.angle);
            ctx.beginPath();
            ctx.arc(0, 0, cellSize * 0.4 * piece.scale, 0, Math.PI * 2);
            const gradient = ctx.createRadialGradient(0, 0, 0, 0, 0, cellSize * 0.4);
            gradient.addColorStop(0, '#FFFFFF');
            gradient.addColorStop(1, progress < 0.5 ? (player === 'black' ? 'white' : 'black') : player);
            ctx.fillStyle = gradient;
            ctx.fill();
            ctx.restore();
        }
        if (animationFrame >= 10) {
            clearInterval(interval);
            flippingPieces.forEach(p => board[p.r][p.c] = player);
            drawBoard();
            playSound(300, 'triangle', 0.15);
            gameState = 'playing';
            flippingPieces = [];
        }
    }, 1000 / 60);
}

function playSound(frequency, type, duration) {
    if (soundLock) return;
    soundLock = true;
    const oscillator = audioCtx.createOscillator();
    oscillator.type = type;
    oscillator.frequency.setValueAtTime(frequency, audioCtx.currentTime);
    oscillator.connect(gainNode);
    oscillator.start();
    oscillator.stop(audioCtx.currentTime + duration);
    setTimeout(() => soundLock = false, duration * 1000 + 50);
}
            

解説

`animateFlip`は各コマの回転角度(`angle`)とスケール(`scale`)を進捗(`progress`)に基づいて更新。`ctx.rotate`と`ctx.scale`で3D効果を、放射グラデーションでキラキラ感を演出。`playSound`は`AudioContext`でオシレーターを生成、音量を`gainNode`で制御。音の重複は`soundLock`で防止し、50msの余裕で安定性確保。アニメーションは約166msで完了し、パフォーマンスを最適化。