Day 19
主題:音效 / 特效 — 成功或失敗時的動畫與音效
今日目標
在前幾天,我們已經完成了翻牌邏輯,卡片能夠生成、翻轉,並進行配對判斷。今天的任務就是為遊戲加入「聲音與視覺特效」,提升玩家的沉浸感。畢竟遊戲除了機制本身,即時的回饋也是影響體驗的重要元素。
學習與操作重點
音效(Audio)
使用 HTML5 的 <audio>
標籤或 JavaScript 的 new Audio()
來播放音效。
建立至少兩種音效:
配對成功 → 輕快的提示音,營造成就感。
配對失敗 → 簡單的錯誤音,提醒玩家。
注意音效檔案大小,避免影響載入速度。
動畫 (CSS + JS)
成功配對的卡片,可以加上閃爍或縮放效果,強調「恭喜找到一對!」。
失敗時,可以加上抖動 (shake)效果,提醒玩家翻錯。
利用 classList.add()
與 setTimeout()
來動態套用 / 移除動畫 class。
細節控制
避免同時播放太多音效,必要時使用 audio.currentTime = 0;
讓音效能立即重新播放。
特效不要過於花俏,以免干擾遊戲的主要體驗。
程式範例
這裡示範如何在配對成功 / 失敗時,加入音效與特效:
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>Day 19 - 音效與特效</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: #fef6e4;
}
.game-board {
display: grid;
grid-template-columns: repeat(4, 100px);
grid-gap: 10px;
}
.card {
width: 100px;
height: 140px;
perspective: 1000px;
cursor: pointer;
}
.card-inner {
width: 100%;
height: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.6s;
}
.card.flip .card-inner {
transform: rotateY(180deg);
}
.front, .back {
position: absolute;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
font-size: 28px;
font-weight: bold;
border-radius: 10px;
backface-visibility: hidden;
}
.front {
background: #8ecae6;
color: white;
}
.back {
background: #ffb703;
color: black;
transform: rotateY(180deg);
}
/* 特效 */
.match {
animation: success 0.6s ease;
}
@keyframes success {
0% { transform: scale(1); }
50% { transform: scale(1.2); }
100% { transform: scale(1); }
}
.shake {
animation: shake 0.4s;
}
@keyframes shake {
0% { transform: translateX(0); }
25% { transform: translateX(-5px); }
50% { transform: translateX(5px); }
75% { transform: translateX(-5px); }
100% { transform: translateX(0); }
}
</style>
</head>
<body>
<div class="game-board" id="gameBoard"></div>
<!-- 音效 -->
<audio id="successSound" src="success.mp3"></audio>
<audio id="failSound" src="fail.mp3"></audio>
<script>
const symbols = ["🍎", "🍌", "🍇", "🍓", "🍍", "🥝"];
let cards = [...symbols, ...symbols];
cards.sort(() => Math.random() - 0.5);
const board = document.getElementById("gameBoard");
const successSound = document.getElementById("successSound");
const failSound = document.getElementById("failSound");
let flippedCards = [];
let lockBoard = false;
// 建立卡片
cards.forEach(symbol => {
const card = document.createElement("div");
card.classList.add("card");
card.innerHTML = `
<div class="card-inner">
<div class="front">?</div>
<div class="back">${symbol}</div>
</div>
`;
card.addEventListener("click", () => {
if (lockBoard || card.classList.contains("flip")) return;
card.classList.add("flip");
flippedCards.push(card);
if (flippedCards.length === 2) {
checkMatch();
}
});
board.appendChild(card);
});
// 檢查配對
function checkMatch() {
lockBoard = true;
const [card1, card2] = flippedCards;
const symbol1 = card1.querySelector(".back").textContent;
const symbol2 = card2.querySelector(".back").textContent;
if (symbol1 === symbol2) {
successSound.currentTime = 0;
successSound.play();
card1.classList.add("match");
card2.classList.add("match");
flippedCards = [];
lockBoard = false;
} else {
failSound.currentTime = 0;
failSound.play();
card1.classList.add("shake");
card2.classList.add("shake");
setTimeout(() => {
card1.classList.remove("flip", "shake");
card2.classList.remove("flip", "shake");
flippedCards = [];
lockBoard = false;
}, 1000);
}
}
</script>
</body>
</html>
今日成果
學會如何在遊戲中加入音效,強化玩家回饋
完成配對成功 / 失敗的特效動畫
遊戲體驗更有「互動感」,不再只是單純的翻牌