4. 技術詳細:主要機能の解説
ここでは、ゲームで使われる主要技術(トランジション、非同期処理、状態管理、イベント駆動、DOM操作、Web Audio API)を、メイン説明後にサンプルコード付きで詳細解説する。各技術がどう機能に組み込まれ、なぜ必要か、どう干渉を防ぐかをガッチリ説明だ!
4.1 CSSトランジション
概要:CSSトランジションは、カードめくりやオーバーレイ(MATCHED!など)の滑らかなアニメーションに使用。`transition`プロパティでプロパティ変化(例:`transform`)を時間指定で補間する。
ゲームでの使用:
- カードめくり:`transform: rotateY(180deg)`で3D回転、0.5秒で滑らかに。
- オーバーレイ:`comboPulse`アニメーションで「N COMBO!」を脈動。
- シャッフル:仮要素の`top`、`left`変化でスライド(JavaScript制御)。
サンプルコード:カードめくりのトランジション(`common.css`)。
.card-inner {
width: 100%;
height: 100%;
transition: transform 0.5s;
transform-style: preserve-3d;
position: absolute;
}
.card.flipped .card-inner {
transform: rotateY(180deg);
}
仕組み:`transition: transform 0.5s`で`transform`変化を0.5秒で補間。`transform-style: preserve-3d`で3D空間を保持、`backface-visibility: hidden`で裏面を非表示。JavaScriptで`flipped`クラスをトグルし、アニメーションをトリガー。
干渉回避:
- `isGameActive`でシャッフル中やクリア時にクリックを無効、不要なトランジションを防止。
- オーバーレイは独立した`div`(`matchOverlay`など)で、カードのアニメーションと層を分離。
- 短いアニメーション時間(0.5~1秒)で、連続操作時の遅延を軽減。
4.2 非同期処理(async/await)
概要:`async/await`は、タイミング依存の処理(アニメーション、シャッフル、サウンド)を順序通りに制御。Promiseを使って非同期タスクを同期的に書ける。
ゲームでの使用:
- シャッフル:`executeShuffle`でスライドアニメーション(0.5秒)を待機後、DOM操作。
- カードめくり:`checkMatch`でハズレ時の1秒待機(`setTimeout`)をPromise化。
- カウントダウン:1秒ごとの数字更新を順序制御。
サンプルコード:シャッフルのスライド(`game.js`)。
async function executeShuffle() {
isGameActive = false;
shuffleOverlay.style.display = "block";
playShuffleSound();
setTimeout(() => shuffleOverlay.style.display = "none", 1000);
if (shufflePair.length === 2) {
const [card1, card2] = shufflePair;
if (!card1.classList.contains("matched") && !card2.classList.contains("matched")) {
const card1Rect = card1.getBoundingClientRect();
const tempCard1 = card1.cloneNode(true);
tempCard1.style.position = "absolute";
tempCard1.style.top = `${card1Rect.top}px`;
tempCard1.style.left = `${card1Rect.left}px`;
tempCard1.style.transition = "all 0.5s ease";
document.body.appendChild(tempCard1);
await new Promise(resolve => setTimeout(() => {
tempCard1.style.top = `${card2Rect.top}px`;
tempCard1.style.left = `${card2Rect.left}px`;
setTimeout(resolve, 500);
}, 100));
// カード交換処理(略)
tempCard1.remove();
}
}
shufflePair = [];
isGameActive = true;
}
仕組み:`await new Promise`でスライド(0.5秒)を待機、DOM操作を確実な順序で実行。`isGameActive`をトグルし、他の非同期処理(めくりなど)をブロック。
干渉回避:
- `isGameActive`でシャッフル中のめくりを無効、同時実行を防止。
- 短い待機時間(0.5~1秒)で、連続シャッフルの遅延を回避。
- Promiseチェーンをシンプルに保ち、複雑な依存関係を排除。
4.3 状態管理
概要:状態管理は、ゲームの進行状況(カード状態、試行数、コンボなど)を追跡。グローバル変数や配列でデータを保持、更新。
ゲームでの使用:
- カード状態:`cards`(全カード)、`flippedCards`(めくられたカード)、`matchedPairs`(ペア数)。
- シャッフル:`shufflePair`で告知済みカードを管理。
- ゲーム進行:`attempts`(試行数)、`comboCount`(コンボ)、`isGameActive`(操作可否)。
サンプルコード:カード状態の初期化(`game.js`)。
function createBoard() {
gameBoard.innerHTML = "";
cards = [];
flippedCards = [];
matchedPairs = 0;
attempts = 0;
comboCount = 0;
shufflePair = [];
isGameActive = true;
// カード生成処理(略)
}
仕組み:`createBoard`で状態をリセット、`cards`にDOM要素を格納、`flippedCards`でめくり状態を追跡。`isGameActive`で全体の制御を一元化。
干渉回避:
- 状態はグローバル変数で一元管理、関数間で共有し不整合を防止。
- `flippedCards`は最大2要素、溢れを防ぐチェックを`flipCard`に実装。
- `isGameActive`でシャッフルやクリア時の状態変更をブロック。
4.4 イベント駆動
概要:イベント駆動は、ユーザー操作(クリック)やシステムイベント(タイマー)をトリガーにロジックを実行。`addEventListener`でハンドラを登録。
ゲームでの使用:
- クリック:カードめくり(`flipCard`)、開始/リセットボタン。
- タイマー:スコア更新(`startTimer`)、カウントダウン、シャッフル告知/交換。
サンプルコード:カードクリック(`game.js`)。
shuffledSymbols.forEach((symbol, index) => {
const card = document.createElement("div");
card.classList.add("card");
card.dataset.symbol = symbol;
// DOM構築(略)
card.addEventListener("click", () => flipCard(card));
gameBoard.appendChild(card);
cards.push(card);
});
仕組み:`addEventListener`で各カードに`flipCard`をバインド、クリックごとに状態を更新。`startTimer`は`setInterval`で毎秒スコアを更新。
干渉回避:
- `isGameActive`で無効なクリックをブロック、シャッフルやクリア時の誤操作を防止。
- イベントハンドラは単一責任(例:`flipCard`はめくりのみ)、複雑なロジックを分離。
- タイマーは`clearInterval`で終了時クリア、メモリリークを回避。
4.5 DOM操作
概要:DOM操作は、カード生成、状態変更(`classList`)、シャッフル交換(`insertBefore`)に使用。`document`や要素のメソッドで動的にUIを更新。
ゲームでの使用:
- カード生成:`createBoard`でグリッド構築。
- 状態変更:`flipped`や`matched`クラスでめくりやペア状態を反映。
- シャッフル:`insertBefore`でカード位置を交換。
サンプルコード:シャッフル交換(`game.js`)。
const parent = gameBoard;
const card1Next = card1.nextSibling;
const card2Next = card2.nextSibling;
if (card1Index < card2Index) {
parent.insertBefore(card2, card1Next);
parent.insertBefore(card1, card2Next || null);
} else {
parent.insertBefore(card1, card2Next);
parent.insertBefore(card2, card1Next || null);
}
仕組み:`insertBefore`で単一スワップ、インデックス順を考慮して正確な交換。`classList`で状態を動的に更新、UIを即反映。
干渉回避:
- `isGameActive`でシャッフル中のDOM操作を制限、めくりとの競合を防止。
- 仮要素(スライド用)は`remove`で即削除、DOMの肥大化を回避。
- インデックスを動的に取得(`Array.from`)、ずれを防止。
4.6 Web Audio API
概要:Web Audio APIは、動的サウンド生成に使用。`AudioContext`でオシレータを制御、波形(サイン、ノコギリなど)や周波数を指定。
ゲームでの使用:
- emojiごとの音:21ペアにユニークな音(例:🔥は600Hzサイン波)。
- コンボ:1000~2050Hz矩形波で音程増加。
- シャッフル:600Hz三角波で交換を強調。
サンプルコード:サウンド生成(`common.js`)。
function playWave(type, frequency, duration, volume = 0.2) {
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = ctx.createOscillator();
const gainNode = ctx.createGain();
oscillator.type = type;
oscillator.frequency.setValueAtTime(frequency, ctx.currentTime);
gainNode.gain.setValueAtTime(volume, ctx.currentTime);
oscillator.connect(gainNode);
gainNode.connect(ctx.destination);
oscillator.start();
oscillator.stop(ctx.currentTime + duration);
}
仕組み:`AudioContext`でオシレータを生成、`type`(波形)、`frequency`(周波数)、`duration`(継続時間)を設定。`gainNode`で音量を制御、即再生・停止。
干渉回避:
- 短い音(0.3~0.5秒)で重複を軽減、同時再生でも聞き分け可能。
- 各音は独立した`AudioContext`インスタンス、メモリ管理を簡潔に。
- サウンドはUI操作(めくり、シャッフル)と並行、タイミング競合なし。