<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>コライドン (Koraidon) 2Dデジタルフィギュア&クイズ</title>
<!-- Tailwind CSS for UI styling -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- FontAwesome for Icons -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
body {
margin: 0;
overflow: hidden;
background-color: #0b0f19;
font-family: 'Helvetica Neue', Arial, 'Hiragino Kaku Gothic ProN', Meiryo, sans-serif;
touch-action: none; /* スマホでの誤スクロール・ピンチズームを防止 */
}
/* スライダーなどのカスタマイズ */
input[type="range"] {
-webkit-appearance: none;
appearance: none;
background: #1e293b;
height: 12px; /* タッチしやすい太さに */
border-radius: 9999px;
border: 1px solid #334155;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 26px; /* タッチターゲットのサイズアップ */
height: 26px;
border-radius: 50%;
background: #ef4444;
cursor: pointer;
border: 3px solid #ffffff;
box-shadow: 0 0 10px rgba(239, 68, 68, 0.5);
transition: transform 0.1s;
}
.no-scrollbar::-webkit-scrollbar {
display: none;
}
.no-scrollbar {
-ms-overflow-style: none;
scrollbar-width: none;
}
/* シルエットを確実に表現するためのカスタムCSS */
.silhouette {
filter: brightness(0) contrast(100%) !important;
-webkit-filter: brightness(0) contrast(100%) !important;
}
/* キャンバスをくっきり表示 */
canvas {
image-rendering: -moz-crisp-edges;
image-rendering: -webkit-crisp-edges;
image-rendering: pixelated;
image-rendering: crisp-edges;
display: block;
touch-action: none;
}
</style>
</head>
<body class="text-slate-100 select-none no-scrollbar">
<!-- 2Dグラフィック描画用 Canvas -->
<canvas id="stage-canvas" class="absolute inset-0 w-full h-full z-0"></canvas>
<!-- UIオーバーレイ -->
<div id="ui-overlay" class="absolute inset-0 pointer-events-none z-10 flex flex-col justify-between p-3 md:p-6 opacity-0 transition-opacity duration-700">
<!-- ヘッダーエリア -->
<header class="flex flex-col sm:flex-row justify-between items-start pointer-events-auto w-full gap-2">
<div class="bg-slate-950/90 border-2 border-red-500 rounded-3xl p-3 md:p-4 shadow-[0_0_20px_rgba(239,68,68,0.2)] max-w-xs md:max-w-sm">
<div class="flex items-center gap-2 mb-1">
<span class="bg-gradient-to-r from-red-600 to-rose-500 text-white text-[9px] md:text-xs px-2 py-0.5 rounded-full font-bold uppercase tracking-widest animate-pulse">2D デジタルフィギュア</span>
<span class="text-yellow-400 text-[10px] md:text-xs font-bold">★パルデアちほう</span>
</div>
<h1 class="text-xl md:text-3xl font-black tracking-wider text-transparent bg-clip-text bg-gradient-to-r from-red-500 via-orange-500 to-yellow-400">
コライドン <span class="text-[10px] text-slate-300 font-normal">Koraidon</span>
</h1>
<p class="text-[10px] md:text-[11px] text-slate-300 mt-1 leading-relaxed">
スワイプ(なぞる)すると むきを かえられるよ!<br>
コライドンを タッチすると ほえるよ!
</p>
</div>
<!-- 右上:ミニゲームスコアとあそびかた -->
<div class="flex flex-row sm:flex-col items-center sm:items-end justify-between w-full sm:w-auto gap-2">
<div class="bg-slate-950/95 border-2 border-yellow-500 rounded-2xl px-3 py-1.5 shadow-lg flex items-center gap-2">
<span class="text-yellow-400 font-extrabold text-xs md:text-sm"><i class="fa-solid fa-star animate-spin mr-1"></i>スコア:</span>
<span id="score-val" class="text-xl font-black text-white font-mono">0</span>
</div>
<div class="flex gap-1.5">
<button id="btn-quiz-open" class="bg-gradient-to-r from-amber-500 to-yellow-500 hover:from-amber-400 hover:to-yellow-400 text-slate-950 font-black px-3.5 py-2 rounded-xl shadow-[0_0_15px_rgba(245,158,11,0.3)] transition transform active:scale-95 text-xs flex items-center gap-1">
<i class="fa-solid fa-gamepad text-base"></i> クイズであそぶ!
</button>
<button id="btn-sound-toggle" class="bg-slate-950/90 hover:bg-slate-900 border-2 border-slate-700 text-white p-2.5 rounded-xl transition shadow-lg flex items-center justify-center" title="おんせいオン/オフ">
<i id="sound-icon" class="fa-solid fa-volume-high text-base text-emerald-400"></i>
</button>
</div>
</div>
</header>
<!-- PokeAPI クイズウィンドウ (ポップアップ) -->
<div id="quiz-window" class="hidden fixed inset-0 bg-black/85 backdrop-blur-sm z-50 flex items-center justify-center p-3 md:p-4 pointer-events-auto">
<div class="bg-slate-950 border-4 border-yellow-400 rounded-3xl p-4 md:p-5 max-w-md w-full shadow-[0_0_50px_rgba(234,179,8,0.3)] relative">
<!-- 閉じるボタン -->
<button id="btn-quiz-close" class="absolute -top-3 -right-3 bg-red-600 hover:bg-red-500 text-white w-10 h-10 rounded-full flex items-center justify-center font-bold border-2 border-white text-lg shadow-lg">
<i class="fa-solid fa-xmark"></i>
</button>
<!-- クイズ本体 -->
<div id="quiz-loading" class="text-center py-10">
<i class="fa-solid fa-circle-notch animate-spin text-4xl text-yellow-400 mb-3"></i>
<p class="text-sm font-bold text-yellow-200">ポケモンの データを よみこんでいるよ...</p>
</div>
<div id="quiz-error" class="hidden text-center py-10 text-red-400">
<i class="fa-solid fa-triangle-exclamation text-4xl mb-3 animate-bounce"></i>
<p class="text-sm font-bold">つうしんが しっぱいしたよ。もういちど試してね。</p>
<button id="btn-quiz-retry" class="mt-4 bg-yellow-400 hover:bg-yellow-300 text-slate-950 px-5 py-2 rounded-lg text-xs font-black shadow-md">もういちどトライ</button>
</div>
<div id="quiz-content" class="hidden space-y-3.5">
<div class="text-center">
<span class="bg-yellow-400 text-slate-950 text-[10px] md:text-xs font-black px-3.5 py-1 rounded-full uppercase tracking-wider">このポケモン だーれだ?</span>
<h3 class="text-sm md:text-base font-black text-slate-200 mt-1.5">シルエットの ポケモンを あててね!</h3>
</div>
<!-- ポケモンの影絵/画像表示エリア -->
<div class="relative bg-slate-900 rounded-2xl p-3 flex items-center justify-center h-40 md:h-44 border-2 border-slate-800 overflow-hidden shadow-inner">
<div class="absolute text-slate-800/10 text-8xl font-black pointer-events-none">?</div>
<!-- ポケモン画像 -->
<img id="quiz-pokemon-img" class="h-32 w-32 md:h-36 md:w-36 object-contain transition-all duration-500 silhouette" src="" alt="Pokemon Silhouette">
</div>
<!-- ヒント情報 -->
<div class="bg-slate-900/80 p-2.5 rounded-xl text-[11px] space-y-1 text-slate-300 border border-slate-800">
<div class="flex justify-between"><span>タイプ:</span> <span id="quiz-pokemon-type" class="font-bold text-yellow-300">-</span></div>
<div class="flex justify-between"><span>おもさ:</span> <span id="quiz-pokemon-weight" class="font-bold text-slate-100">-</span></div>
</div>
<!-- 選択肢ボタン (4択) -->
<div class="grid grid-cols-2 gap-2" id="quiz-options">
<!-- JSで動的にボタンを生成します -->
</div>
<!-- 結果表示エリア -->
<div id="quiz-feedback" class="hidden text-center p-2.5 rounded-xl font-bold text-xs md:text-sm border-2 transition duration-300">
<p id="feedback-text"></p>
<button id="btn-next-quiz" class="mt-2 bg-yellow-400 hover:bg-yellow-300 text-slate-950 px-6 py-2 rounded-lg text-xs font-black shadow-md transition">
つぎのクイズへ! <i class="fa-solid fa-arrow-right"></i>
</button>
</div>
</div>
</div>
</div>
<!-- 下部のコントロールパネル -->
<div class="flex flex-col md:flex-row gap-3 justify-between items-end w-full pointer-events-auto">
<!-- 左下:背景(はいけい)えらび -->
<div class="bg-slate-950/95 border-2 border-red-500/30 rounded-2xl p-3 shadow-xl max-w-xs hidden md:block">
<div class="text-xs font-bold text-rose-400 uppercase tracking-wider mb-2 flex items-center gap-1.5">
<i class="fa-solid fa-mountain-sun"></i>
<span>ステージ(背景)を かえよう!</span>
</div>
<div class="grid grid-cols-3 gap-1.5">
<button class="bg-sky-950/80 hover:bg-sky-900 border border-sky-500 text-sky-300 py-1 px-2 rounded-lg font-bold text-[10px] btn-bg-stage" data-bg="day">はれ</button>
<button class="bg-amber-950/80 hover:bg-amber-900 border border-amber-500 text-amber-300 py-1 px-2 rounded-lg font-bold text-[10px] btn-bg-stage" data-bg="sunset">ゆうがた</button>
<button class="bg-indigo-950/80 hover:bg-indigo-900 border border-indigo-500 text-indigo-300 py-1 px-2 rounded-lg font-bold text-[10px] btn-bg-stage" data-bg="night">よぞら</button>
</div>
</div>
<!-- 右下:コライドン 2Dコントロールボード -->
<div class="bg-slate-950/95 border-2 border-red-500/50 rounded-3xl p-3 md:p-4 shadow-2xl w-full md:max-w-md">
<div class="flex items-center justify-between mb-2 border-b border-slate-800 pb-1.5">
<div class="flex items-center gap-1.5">
<i class="fa-solid fa-dragon text-rose-500 text-base animate-bounce"></i>
<span class="font-black text-xs md:text-sm text-slate-100">コライドンを うごかそう!</span>
</div>
<!-- スマホ向けに背景切り替えをミニボタン化して格納 -->
<div class="flex md:hidden gap-1">
<button class="bg-sky-950 border border-sky-500 text-sky-300 px-1.5 py-0.5 rounded text-[9px] font-bold btn-bg-stage" data-bg="day">はれ</button>
<button class="bg-amber-950 border border-amber-500 text-amber-300 px-1.5 py-0.5 rounded text-[9px] font-bold btn-bg-stage" data-bg="sunset">夕方</button>
<button class="bg-indigo-950 border border-indigo-500 text-indigo-300 px-1.5 py-0.5 rounded text-[9px] font-bold btn-bg-stage" data-bg="night">夜空</button>
</div>
</div>
<!-- スライダーコントロール -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-2 text-[11px] mb-2.5">
<!-- アニメーション速度 -->
<div>
<div class="flex justify-between mb-0.5 text-slate-300 font-bold">
<span>うごく はやさ</span>
<span class="text-rose-400 font-black" id="val-speed">1.0x</span>
</div>
<input type="range" id="slider-speed" min="0" max="2" step="0.1" value="1" class="w-full">
</div>
<!-- エフェクトの強さ -->
<div class="hidden sm:block">
<div class="flex justify-between mb-0.5 text-slate-300 font-bold">
<span>エフェクトの強さ</span>
<span class="text-rose-400 font-black" id="val-effects">1.0x</span>
</div>
<input type="range" id="slider-effects" min="0.5" max="2.0" step="0.1" value="1.0" class="w-full">
</div>
</div>
<!-- モーション・ポーズボタン -->
<div class="space-y-1">
<div class="grid grid-cols-3 gap-1.5">
<button id="pose-stand" class="bg-rose-600 hover:bg-rose-500 text-white py-2 px-1 rounded-xl font-bold transition duration-200 shadow-md text-xs">
<i class="fa-solid fa-paw mr-1"></i>たたずむ
</button>
<button id="pose-flight" class="bg-slate-900 hover:bg-slate-800 text-slate-300 py-2 px-1 rounded-xl font-bold transition duration-200 text-xs">
<i class="fa-solid fa-wind mr-1"></i>とぶ!
</button>
<button id="pose-glow" class="bg-slate-900 hover:bg-slate-800 text-slate-300 py-2 px-1 rounded-xl font-bold transition duration-200 text-xs">
<i class="fa-solid fa-volume-high mr-1"></i>ほえる!
</button>
</div>
</div>
</div>
</div>
</div>
<!-- MAIN INTERACTIVE RENDERING LOGIC -->
<script>
const canvas = document.getElementById('stage-canvas');
const ctx = canvas.getContext('2d');
let speedMultiplier = 1.0;
let effectsIntensity = 1.0;
let currentPose = 'stand'; // 'stand', 'flight', 'glow'
let currentBgStage = 'day'; // 'day', 'sunset', 'night'
let speakEnabled = true;
// モバイル向け:ドラッグ・タッチ操作による左右回転パラメータ
let koraidonRotateY = 0; // スワイプした時の追加回転角
let isDragging = false;
let startX = 0;
// 音声の最初のインタラクション解放フラグ
let audioUnlocked = false;
// タイマー管理
let lastTime = 0;
let totalTime = 0;
// 咆哮モード用の音波リング管理配列
let roarRings = [];
// 星屑(粒子)エフェクトの配列
const particles = [];
for (let i = 0; i < 35; i++) {
particles.push({
x: Math.random(),
y: Math.random(),
size: 1 + Math.random() * 3,
speedX: 0.1 + Math.random() * 0.3,
speedY: 0.05 + Math.random() * 0.15,
opacity: 0.2 + Math.random() * 0.6
});
}
// コライドン・カラーパレット
const COLOR = {
scarlet: '#d61c22', // 緋色・メインの赤
scarletLight: '#f25a5a', // あかるいお腹の赤
cream: '#f5f0e1', // 爪やアンテナのクリーム白
white: '#ffffff', // 純白
blue: '#004e98', // トサカ・マントのロイヤルブルー
darkBlue: '#0a1128', // タイヤの中の暗色
magenta: '#d81159', // リボン根元の鮮烈なピンク
grey: '#1c1c1e', // タイヤのゴム色
gold: '#fca311', // 金色の目・模様アクセント
orange: '#f77f00', // 瞳孔周辺のオレンジ
pupil: '#0e0e11' // 漆黒
};
// キャンバスサイズ自動調整(Retina対応)
function resizeCanvas() {
const dpr = window.devicePixelRatio || 1;
canvas.width = window.innerWidth * dpr;
canvas.height = window.innerHeight * dpr;
// CSSサイズをデバイス幅に固定
canvas.style.width = window.innerWidth + 'px';
canvas.style.height = window.innerHeight + 'px';
// コンテキストの描画比率をデバイス比に合わせる
ctx.scale(dpr, dpr);
}
window.addEventListener('resize', resizeCanvas);
resizeCanvas();
// いなずまダイヤマークを描く便利なヘルパー関数
function drawLightningMark(ctx, x, y, size, angle) {
ctx.save();
ctx.translate(x, y);
ctx.rotate(angle);
ctx.fillStyle = COLOR.white;
ctx.beginPath();
ctx.moveTo(0, -size);
ctx.lineTo(size * 0.4, -size * 0.2);
ctx.lineTo(size * 0.1, 0);
ctx.lineTo(size * 0.5, size * 0.6);
ctx.lineTo(-size * 0.1, size);
ctx.lineTo(-size * 0.2, size * 0.2);
ctx.lineTo(0, 0);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// つやつやしたグラデーション付きの丸みのあるボディを描く(2Dベクター)
function drawKoraidon(ctx, cx, cy, scale, t) {
ctx.save();
ctx.translate(cx, cy);
ctx.scale(scale, scale);
// ドラッグ(スワイプ)による回転角を適用
ctx.rotate(koraidonRotateY);
// ポーズごとのボディの配置調整
let bodyX = 0;
let bodyY = 0;
let bodyRotation = 0;
let headX = 60;
let headY = -120;
let headRotation = 0;
let tailRotation = 0;
// 飛行状態・咆哮状態による変化
if (currentPose === 'stand') {
bodyY = Math.sin(t * 3.5) * 5;
headY += Math.cos(t * 3) * 3;
tailRotation = Math.sin(t * 2) * 0.15;
} else if (currentPose === 'flight') {
bodyY = Math.sin(t * 6.0) * 12;
bodyRotation = -0.15 + Math.sin(t * 5) * 0.05;
headX = 80;
headY = -100;
headRotation = 0.1 + Math.cos(t * 6.0) * 0.05;
tailRotation = Math.sin(t * 8.0) * 0.4;
} else if (currentPose === 'glow') {
bodyX = (Math.random() - 0.5) * 2;
bodyY = (Math.random() - 0.5) * 2;
headY = -140;
headRotation = -0.4 + Math.sin(t * 12.0) * 0.04; // 上を向く!
tailRotation = Math.sin(t * 10) * 0.25;
}
ctx.translate(bodyX, bodyY);
ctx.rotate(bodyRotation);
// 1. 【しっぽ (Tail)】
ctx.save();
ctx.translate(-120, -10);
ctx.rotate(tailRotation);
// 節々のうねりを描く
for (let i = 0; i < 5; i++) {
ctx.translate(-25, 10);
ctx.rotate(Math.sin(t * 3 + i) * 0.1);
// 節本体
let segGrad = ctx.createLinearGradient(0, -20, 0, 20);
segGrad.addColorStop(0, COLOR.scarletLight);
segGrad.addColorStop(1, COLOR.scarlet);
ctx.fillStyle = segGrad;
ctx.beginPath();
ctx.arc(0, 0, 28 - i * 4, 0, Math.PI * 2);
ctx.fill();
// 棘(トゲ)
ctx.fillStyle = (i % 2 === 0) ? COLOR.blue : COLOR.cream;
ctx.beginPath();
ctx.moveTo(0, -25 + i * 3);
ctx.lineTo(-12, 0);
ctx.lineTo(12, 0);
ctx.closePath();
ctx.fill();
}
ctx.restore();
// 2. 【後肢 (Back Leg)】
ctx.save();
ctx.translate(-70, 30);
if (currentPose === 'flight') {
ctx.rotate(Math.PI / 3); // 飛行中は折りたたむ
} else {
ctx.rotate(Math.sin(t * 3) * 0.05);
}
// 腿
let thighGrad = ctx.createRadialGradient(-10, -10, 5, 0, 0, 45);
thighGrad.addColorStop(0, COLOR.scarletLight);
thighGrad.addColorStop(1, COLOR.scarlet);
ctx.fillStyle = thighGrad;
ctx.beginPath();
ctx.ellipse(0, 0, 35, 50, Math.PI/6, 0, Math.PI * 2);
ctx.fill();
// 太ももの白い稲妻マーク
drawLightningMark(ctx, 15, -10, 18, -Math.PI / 10);
// 下腿・足
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
ctx.ellipse(10, 45, 18, 30, -Math.PI/12, 0, Math.PI * 2);
ctx.fill();
// 白い3本爪
ctx.fillStyle = COLOR.cream;
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.arc(20 + i * 8, 70, 7, 0, Math.PI, true);
ctx.closePath();
ctx.fill();
}
ctx.restore();
// 3. 【胴体 (Torso)】
// 陰影を表現する豪華なグラデーション
let bodyGrad = ctx.createLinearGradient(-120, -50, 100, 50);
bodyGrad.addColorStop(0, COLOR.scarlet);
bodyGrad.addColorStop(0.5, COLOR.scarletLight);
bodyGrad.addColorStop(1, COLOR.scarlet);
ctx.fillStyle = bodyGrad;
ctx.beginPath();
ctx.ellipse(0, -10, 110, 60, Math.PI / 16, 0, Math.PI * 2);
ctx.fill();
// お腹の白い毛フサフサ
ctx.fillStyle = COLOR.cream;
ctx.beginPath();
ctx.ellipse(30, 20, 50, 35, -Math.PI / 12, 0, Math.PI * 2);
ctx.fill();
// 4. 【お尻タイヤ (Rear Wheel - 収縮状態)】
ctx.save();
ctx.translate(-100, 20);
let wheelRot = (currentPose === 'flight') ? t * 8.0 : t * 0.3;
ctx.rotate(wheelRot);
// 外のゴム
ctx.fillStyle = COLOR.grey;
ctx.beginPath();
ctx.arc(0, 0, 45, 0, Math.PI * 2);
ctx.fill();
// ハブ
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
ctx.arc(0, 0, 25, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = COLOR.cream;
ctx.beginPath();
ctx.arc(0, 0, 12, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 5. 【前肢 (Front Leg)】
ctx.save();
ctx.translate(65, 20);
if (currentPose === 'flight') {
ctx.rotate(Math.PI / 2.5); // 後ろになびく
} else if (currentPose === 'glow') {
ctx.rotate(-Math.PI / 12);
}
// 肩
let shoulderGrad = ctx.createRadialGradient(-5, -5, 5, 0, 0, 35);
shoulderGrad.addColorStop(0, COLOR.scarletLight);
shoulderGrad.addColorStop(1, COLOR.scarlet);
ctx.fillStyle = shoulderGrad;
ctx.beginPath();
ctx.ellipse(0, 0, 28, 40, -Math.PI / 8, 0, Math.PI * 2);
ctx.fill();
// 肩の稲妻マーク
drawLightningMark(ctx, -10, -5, 14, Math.PI / 8);
// 腕・前脚
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
ctx.ellipse(5, 38, 16, 28, Math.PI / 12, 0, Math.PI * 2);
ctx.fill();
// 爪
ctx.fillStyle = COLOR.cream;
for (let i = 0; i < 3; i++) {
ctx.beginPath();
ctx.arc(10 + i * 8, 62, 6, 0, Math.PI, true);
ctx.closePath();
ctx.fill();
}
ctx.restore();
// 6. 【巨大な胸部タイヤ (Chest Wheel)】
ctx.save();
ctx.translate(55, 35);
let chestWheelRot = 0;
if (currentPose === 'stand') {
chestWheelRot = t * 0.4;
} else if (currentPose === 'flight') {
chestWheelRot = t * 9.0; // 超高速回転!
} else if (currentPose === 'glow') {
chestWheelRot = t * 2.0;
}
ctx.rotate(chestWheelRot);
// 外側の黒いゴムタイヤ
ctx.fillStyle = COLOR.grey;
ctx.beginPath();
ctx.arc(0, 0, 68, 0, Math.PI * 2);
ctx.fill();
// タイヤのトレッド(外側のボコボコトゲトゲ)
ctx.fillStyle = COLOR.grey;
for (let i = 0; i < 10; i++) {
let angle = (i / 10) * Math.PI * 2;
ctx.save();
ctx.rotate(angle);
ctx.beginPath();
ctx.moveTo(63, -12);
ctx.lineTo(76, 0);
ctx.lineTo(63, 12);
ctx.closePath();
ctx.fill();
ctx.restore();
}
// 白い歯車(インナーホイール)
ctx.fillStyle = COLOR.cream;
ctx.beginPath();
ctx.arc(0, 0, 48, 0, Math.PI * 2);
ctx.fill();
// 特徴的な赤のギザギザ・太陽星型マークを数学的パスで精密描画!
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
const points = 8;
for (let i = 0; i < points * 2; i++) {
let angle = (i / (points * 2)) * Math.PI * 2;
let r = (i % 2 === 0) ? 46 : 22; // ギザギザの深さ
ctx.lineTo(Math.cos(angle) * r, Math.sin(angle) * r);
}
ctx.closePath();
ctx.fill();
// 中央の赤いハブ
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
ctx.arc(0, 0, 18, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = COLOR.cream;
ctx.beginPath();
ctx.arc(0, 0, 8, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 7. 【首 (Neck)】
ctx.save();
ctx.translate(headX - 10, headY + 30);
ctx.rotate(-0.25 + Math.sin(t * 3.5) * 0.03);
let neckGrad = ctx.createLinearGradient(-25, -25, 25, 25);
neckGrad.addColorStop(0, COLOR.scarletLight);
neckGrad.addColorStop(1, COLOR.scarlet);
ctx.fillStyle = neckGrad;
ctx.beginPath();
ctx.ellipse(0, 0, 22, 45, Math.PI / 4, 0, Math.PI * 2);
ctx.fill();
// ディープブルーの肩ケープマント
ctx.fillStyle = COLOR.blue;
ctx.beginPath();
ctx.ellipse(-15, 20, 20, 35, Math.PI / 3, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 8. 【頭部グループ (Head Group)】
ctx.save();
ctx.translate(headX, headY);
ctx.rotate(headRotation);
// 頭のベース
let headGrad = ctx.createRadialGradient(-10, -10, 5, 0, 0, 40);
headGrad.addColorStop(0, COLOR.scarletLight);
headGrad.addColorStop(1, COLOR.scarlet);
ctx.fillStyle = headGrad;
ctx.beginPath();
ctx.ellipse(0, 0, 38, 30, 0, 0, Math.PI * 2);
ctx.fill();
// 口先(アゴ)
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
ctx.moveTo(10, -15);
ctx.lineTo(45, 5);
// 咆哮モードなら大きく口を開ける!
if (currentPose === 'glow') {
ctx.lineTo(25, 32);
ctx.lineTo(0, 12);
} else {
ctx.lineTo(25, 12);
ctx.lineTo(0, 8);
}
ctx.closePath();
ctx.fill();
// アゴの白いひげ飾り
ctx.fillStyle = COLOR.cream;
ctx.beginPath();
ctx.ellipse(15, 18, 15, 8, -Math.PI / 12, 0, Math.PI * 2);
ctx.fill();
// 青いトサカ(王冠風)
ctx.fillStyle = COLOR.blue;
ctx.beginPath();
ctx.moveTo(-15, -25);
ctx.lineTo(-5, -45);
ctx.lineTo(5, -25);
ctx.lineTo(20, -38);
ctx.lineTo(22, -15);
ctx.closePath();
ctx.fill();
// 眉元の赤いトゲ
ctx.fillStyle = COLOR.scarlet;
ctx.beginPath();
ctx.moveTo(15, -20);
ctx.lineTo(35, -28);
ctx.lineTo(25, -10);
ctx.closePath();
ctx.fill();
// 【瞳 (Eye - 二重構造のうるみ表現)】
ctx.save();
ctx.translate(14, -6);
// 白目(オレンジ)
ctx.fillStyle = COLOR.gold;
ctx.beginPath();
ctx.ellipse(0, 0, 14, 10, -Math.PI / 12, 0, Math.PI * 2);
ctx.fill();
// 黒目(瞳孔)
ctx.fillStyle = COLOR.pupil;
ctx.beginPath();
ctx.ellipse(3, 0, 7, 7, 0, 0, Math.PI * 2);
ctx.fill();
// ハイライト(白)
ctx.fillStyle = COLOR.white;
ctx.beginPath();
ctx.arc(5, -3, 3, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 【超ロング・うねうねリボンアンテナ】
// 頭の後ろから優雅にうねって背中に垂れるグラデーション付きロングアンテナ
ctx.save();
ctx.translate(-20, -5);
let ribbonSegments = 16;
for (let i = 0; i < ribbonSegments; i++) {
// セグメントを重ねることで、うねうねした有機的な厚みを完全表現
ctx.translate(-12, 5 + Math.sin(t * 4 + i * 0.4) * 2.5);
ctx.rotate(-0.06 + Math.cos(t * 3.5 + i * 0.3) * 0.04);
// グラデーション色(根元はビビッドマゼンタピンク、先端は白)
let segColor = COLOR.cream;
if (i < 4) {
segColor = COLOR.magenta;
} else if (i < 8) {
segColor = COLOR.white;
}
ctx.fillStyle = segColor;
ctx.beginPath();
// 扁平な節
ctx.ellipse(0, 0, 15 - i * 0.6, 6 - i * 0.2, Math.PI / 12, 0, Math.PI * 2);
ctx.fill();
}
ctx.restore();
ctx.restore(); // 頭部グループ復帰
ctx.restore(); // 親グループ復帰
}
// ----------------------------------------------------
// --- 咆哮(ほうこう)エフェクト発生処理 ---
// ----------------------------------------------------
function triggerRoarEffect() {
roarRings.push({
x: canvas.width / (2 * (window.devicePixelRatio || 1)) + 100 * koraidonScaleFactor(),
y: canvas.height / (2 * (window.devicePixelRatio || 1)) - 130 * koraidonScaleFactor(),
radius: 10,
maxRadius: 180 + Math.random() * 120,
opacity: 1.0,
color: COLOR.magenta
});
// 2層目
setTimeout(() => {
roarRings.push({
x: canvas.width / (2 * (window.devicePixelRatio || 1)) + 100 * koraidonScaleFactor(),
y: canvas.height / (2 * (window.devicePixelRatio || 1)) - 130 * koraidonScaleFactor(),
radius: 10,
maxRadius: 220,
opacity: 0.8,
color: COLOR.gold
});
}, 150);
}
// 画面幅からスケーリング比を算出
function koraidonScaleFactor() {
let scale = 1.25;
if (window.innerWidth < 640) {
scale = 0.85; // スマホ
} else if (window.innerWidth < 1024) {
scale = 1.05; // タブレット
}
return scale;
}
// ----------------------------------------------------
// --- メインループ & レンダリング ---
// ----------------------------------------------------
function drawStage(timestamp) {
if (!lastTime) lastTime = timestamp;
const dt = (timestamp - lastTime) / 1000;
lastTime = timestamp;
// アニメーション用時間を蓄積
totalTime += dt * speedMultiplier;
// 描画領域をクリア(デバイス比率を考慮した実際の論理ピクセル範囲)
const w = window.innerWidth;
const h = window.innerHeight;
ctx.clearRect(0, 0, w, h);
// 1. 【背景ステージの描画】
let bgGrad = ctx.createLinearGradient(0, 0, 0, h);
if (currentBgStage === 'day') {
bgGrad.addColorStop(0, '#0f172a'); // さわやかな宇宙ブルー
bgGrad.addColorStop(0.6, '#1e1b4b');
bgGrad.addColorStop(1, '#311042');
} else if (currentBgStage === 'sunset') {
bgGrad.addColorStop(0, '#2e1035'); // 燃えるような夕空
bgGrad.addColorStop(0.5, '#7c2d12');
bgGrad.addColorStop(1, '#f97316');
} else if (currentBgStage === 'night') {
bgGrad.addColorStop(0, '#030712'); // 深い夜空
bgGrad.addColorStop(0.6, '#0b0f19');
bgGrad.addColorStop(1, '#111827');
}
ctx.fillStyle = bgGrad;
ctx.fillRect(0, 0, w, h);
// 2. 【飛行モード時の流れる光(スピードライン)】
if (currentPose === 'flight') {
ctx.strokeStyle = 'rgba(255, 255, 255, 0.12)';
ctx.lineWidth = 2;
for (let i = 0; i < 8; i++) {
let lineY = ((totalTime * 800 + i * 200) % h);
ctx.beginPath();
ctx.moveTo(0, lineY);
ctx.lineTo(w, lineY - 150);
ctx.stroke();
}
}
// 3. 【浮遊する粒子エフェクト】
particles.forEach(p => {
p.y -= p.speedY * speedMultiplier * effectsIntensity;
p.x += Math.sin(totalTime + p.size) * 0.0005;
if (p.y < 0) {
p.y = 1.0;
p.x = Math.random();
}
ctx.fillStyle = (currentBgStage === 'sunset') ? 'rgba(249, 115, 22, 0.5)' : 'rgba(239, 68, 68, 0.5)';
ctx.beginPath();
ctx.arc(p.x * w, p.y * h, p.size * effectsIntensity, 0, Math.PI * 2);
ctx.fill();
});
// 4. 【お立ち台プレート (展示スタンド)】
const cx = w / 2;
const cy = h / 2 + 100;
ctx.save();
ctx.shadowBlur = 30;
ctx.shadowColor = 'rgba(239, 68, 68, 0.4)';
let stageGrad = ctx.createRadialGradient(cx, cy, 10, cx, cy, 320);
stageGrad.addColorStop(0, '#111625');
stageGrad.addColorStop(0.8, '#090d16');
stageGrad.addColorStop(1, 'rgba(15, 23, 42, 0)');
ctx.fillStyle = stageGrad;
ctx.beginPath();
ctx.ellipse(cx, cy, 340, 100, 0, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
// 5. 【コライドン本体の描画】
let koraidonScale = koraidonScaleFactor();
drawKoraidon(ctx, cx - 40 * koraidonScale, cy - 10 * koraidonScale, koraidonScale, totalTime);
// 6. 【咆哮音波リングの描画】
roarRings.forEach((ring, index) => {
ring.radius += 5 * speedMultiplier;
ring.opacity = 1 - (ring.radius / ring.maxRadius);
if (ring.radius >= ring.maxRadius) {
roarRings.splice(index, 1);
} else {
ctx.save();
ctx.strokeStyle = ring.color;
ctx.globalAlpha = ring.opacity;
ctx.lineWidth = 4 * effectsIntensity;
ctx.shadowBlur = 15;
ctx.shadowColor = ring.color;
ctx.beginPath();
ctx.arc(ring.x, ring.y, ring.radius, 0, Math.PI * 2);
ctx.stroke();
ctx.restore();
}
});
// 咆哮モード中は定期的(0.3秒おき)に自動でリングエフェクトを射出
if (currentPose === 'glow' && Math.floor(totalTime * 3) % 4 === 0 && roarRings.length < 5) {
triggerRoarEffect();
}
requestAnimationFrame(drawStage);
}
// ----------------------------------------------------
// --- モバイル&デスクトップ共通:スワイプ(回転)とタップ判定 ---
// ----------------------------------------------------
function setupTouchHandlers() {
// ドラッグ開始
function handleStart(clientX) {
isDragging = true;
startX = clientX;
unlockAudio(); // 最初のタップでWebAudioロックを解除
}
// ドラッグ中(左右スワイプでコライドン回転)
function handleMove(clientX) {
if (!isDragging) return;
const deltaX = clientX - startX;
koraidonRotateY += deltaX * 0.007; // スピード調整
startX = clientX;
}
// ドラッグ終了(簡易タップ判定)
function handleEnd() {
isDragging = false;
}
// タップして「ほえる!」を発動
function handleTap(clientX, clientY) {
// クイズ表示中、またはUIコントロールを触っている時は動作させない
if (document.getElementById('quiz-window').classList.contains('hidden') === false) return;
// コライドンがいると思われる画面中央付近
const cx = window.innerWidth / 2;
const cy = window.innerHeight / 2;
const dist = Math.hypot(clientX - cx, clientY - cy);
// コライドンを直接タッチした場合に「ほえる」ポーズを発動
if (dist < 150 * koraidonScaleFactor()) {
clearPoseBtnStyles();
document.getElementById('pose-glow').className = "bg-amber-500 hover:bg-amber-400 text-white py-2.5 px-1 rounded-xl font-bold transition duration-200 text-xs animate-pulse";
currentPose = 'glow';
triggerRoarEffect();
playSystemSound(150, 'sawtooth', 0.3);
}
}
// マウスイベント
canvas.addEventListener('mousedown', (e) => {
handleStart(e.clientX);
});
window.addEventListener('mousemove', (e) => {
handleMove(e.clientX);
});
window.addEventListener('mouseup', () => {
handleEnd();
});
canvas.addEventListener('click', (e) => {
handleTap(e.clientX, e.clientY);
});
// タッチイベント
canvas.addEventListener('touchstart', (e) => {
if (e.touches.length === 1) {
handleStart(e.touches[0].clientX);
}
}, { passive: true });
canvas.addEventListener('touchmove', (e) => {
if (e.touches.length === 1) {
handleMove(e.touches[0].clientX);
}
}, { passive: true });
canvas.addEventListener('touchend', (e) => {
handleEnd();
if (e.changedTouches.length === 1) {
// タップとして判定
handleTap(e.changedTouches[0].clientX, e.changedTouches[0].clientY);
}
}, { passive: true });
}
// 初期化実行
document.addEventListener('DOMContentLoaded', () => {
// UI表示
setTimeout(() => {
document.getElementById('ui-overlay').classList.remove('opacity-0');
}, 100);
// ループ&操作の初期化
setupTouchHandlers();
requestAnimationFrame(drawStage);
});
</script>
<!-- UI INTERACTIVE BINDINGS & POKEAPI QUIZ -->
<script>
// --- 調整スライダーバインディング ---
document.getElementById('slider-speed').addEventListener('input', (e) => {
speedMultiplier = parseFloat(e.target.value);
document.getElementById('val-speed').textContent = speedMultiplier.toFixed(1) + 'x';
});
const sliderEffects = document.getElementById('slider-effects');
if (sliderEffects) {
sliderEffects.addEventListener('input', (e) => {
effectsIntensity = parseFloat(e.target.value);
document.getElementById('val-effects').textContent = effectsIntensity.toFixed(1) + 'x';
});
}
// --- ポーズ切り替えイベント ---
const btnStand = document.getElementById('pose-stand');
const btnFlight = document.getElementById('pose-flight');
const btnGlow = document.getElementById('pose-glow');
function clearPoseBtnStyles() {
[btnStand, btnFlight, btnGlow].forEach(btn => {
btn.className = "bg-slate-900 hover:bg-slate-800 text-slate-300 py-2.5 px-1 rounded-xl font-bold transition duration-200 text-xs";
});
}
btnStand.addEventListener('click', () => {
clearPoseBtnStyles();
btnStand.className = "bg-rose-600 hover:bg-rose-500 text-white py-2.5 px-1 rounded-xl font-bold transition duration-200 shadow-md text-xs";
currentPose = 'stand';
playSystemSound(440, 'sine', 0.1);
});
btnFlight.addEventListener('click', () => {
clearPoseBtnStyles();
btnFlight.className = "bg-sky-600 hover:bg-sky-500 text-white py-2.5 px-1 rounded-xl font-bold transition duration-200 shadow-md text-xs";
currentPose = 'flight';
playSystemSound(554, 'triangle', 0.15);
});
btnGlow.addEventListener('click', () => {
clearPoseBtnStyles();
btnGlow.className = "bg-amber-500 hover:bg-amber-400 text-white py-2.5 px-1 rounded-xl font-bold transition duration-200 shadow-md text-xs animate-pulse";
currentPose = 'glow';
triggerRoarEffect();
playSystemSound(150, 'sawtooth', 0.3); // 咆哮っぽいうなり音
});
// 背景切り替えボタンのバインディング
document.querySelectorAll('.btn-bg-stage').forEach(btn => {
btn.addEventListener('click', (e) => {
currentBgStage = btn.getAttribute('data-bg');
playSystemSound(659, 'sine', 0.05);
});
});
// 音声オンオフ切り替え
const soundBtn = document.getElementById('btn-sound-toggle');
const soundIcon = document.getElementById('sound-icon');
soundBtn.addEventListener('click', () => {
speakEnabled = !speakEnabled;
if (speakEnabled) {
soundIcon.className = "fa-solid fa-volume-high text-lg text-emerald-400";
playSystemSound(440, 'sine', 0.08);
} else {
soundIcon.className = "fa-solid fa-volume-xmark text-lg text-rose-500";
}
});
// モバイル(iOSなど)のセキュリティロック解除用関数
function unlockAudio() {
if (audioUnlocked) return;
try {
// ダミーのWebAudioを一度再生してブラウザの音声を解放する
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const buffer = audioCtx.createBuffer(1, 1, 22050);
const source = audioCtx.createBufferSource();
source.buffer = buffer;
source.connect(audioCtx.destination);
source.start();
// 空のテキストスピーチを発行して音声合成も解放
if ('speechSynthesis' in window) {
const u = new SpeechSynthesisUtterance("");
window.speechSynthesis.speak(u);
}
audioUnlocked = true;
console.log("Web Audio & SpeechSynthesis Unlocked.");
} catch (e) {
console.log("Audio unlock failed:", e);
}
}
// おんせいごうせい & 効果音
function playSystemSound(freq, type, duration) {
if (!speakEnabled) return;
try {
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.type = type || 'sine';
osc.frequency.setValueAtTime(freq, audioCtx.currentTime);
gain.gain.setValueAtTime(0.15, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.01, audioCtx.currentTime + duration);
osc.connect(gain);
gain.connect(audioCtx.destination);
osc.start();
osc.stop(audioCtx.currentTime + duration);
} catch (e) {
console.log("Audio failed to play");
}
}
// ----------------------------------------------------
// --- PokeAPI クイズシステム ---
// ----------------------------------------------------
const TYPE_JA = {
normal: "ノーマル", fire: "ほのお", water: "みず", grass: "くさ",
electric: "でんき", ice: "こおり", fighting: "かくとう", poison: "どく",
ground: "じめん", flying: "ひこう", psychic: "エスパー", bug: "むし",
rock: "いわ", ghost: "ゴースト", dragon: "ドラゴン", steel: "はがね",
fairy: "フェアリー", dark: "あく"
};
const QuizGame = {
score: 0,
currentCorrect: null,
init: function() {
document.getElementById('btn-quiz-open').addEventListener('click', () => {
document.getElementById('quiz-window').classList.remove('hidden');
this.loadNewQuestion();
});
document.getElementById('btn-quiz-close').addEventListener('click', () => {
document.getElementById('quiz-window').classList.add('hidden');
});
document.getElementById('btn-next-quiz').addEventListener('click', () => {
this.loadNewQuestion();
});
document.getElementById('btn-quiz-retry').addEventListener('click', () => {
this.loadNewQuestion();
});
},
showState: function(state) {
['loading', 'content', 'error', 'feedback'].forEach(s => {
document.getElementById('quiz-' + s).classList.add('hidden');
});
document.getElementById('quiz-' + state).classList.remove('hidden');
},
loadNewQuestion: async function() {
this.showState('loading');
document.getElementById('quiz-feedback').classList.add('hidden');
const imgEl = document.getElementById('quiz-pokemon-img');
imgEl.src = "";
imgEl.className = "h-32 w-32 md:h-36 md:w-36 object-contain silhouette"; // 黒塗り(影絵)化
try {
// 画像と日本語データが100%揃っている第1世代〜第7世代(1〜807番)からランダムに4つ用意
const ids = [];
while(ids.length < 4) {
const rid = Math.floor(Math.random() * 807) + 1;
if(!ids.includes(rid)) ids.push(rid);
}
// 4匹の基本データを同時読み込み
const details = await Promise.all(ids.map(id =>
fetch(`https://pokeapi.co/api/v2/pokemon/${id}`).then(r => {
if (!r.ok) throw new Error("APIエラー");
return r.json();
})
));
this.currentCorrect = details[0];
// 種族名(日本語名)の取得
const speciesData = await fetch(this.currentCorrect.species.url).then(r => r.json());
this.currentCorrect.nameJa = speciesData.names.find(n => n.language.name === "ja-Hrkt")?.name
|| speciesData.names.find(n => n.language.name === "ja")?.name
|| this.currentCorrect.name;
// 他の3つのハズレ選択肢
const optionNames = [this.currentCorrect.nameJa];
for (let i = 1; i < 4; i++) {
const sData = await fetch(details[i].species.url).then(r => r.json());
const nameJa = sData.names.find(n => n.language.name === "ja-Hrkt")?.name || details[i].name;
optionNames.push(nameJa);
}
// 選択肢のシャッフル
optionNames.sort(() => Math.random() - 0.5);
// ヒント情報の登録
document.getElementById('quiz-pokemon-type').textContent = this.currentCorrect.types.map(t => TYPE_JA[t.type.name] || t.type.name).join(' ・ ');
document.getElementById('quiz-pokemon-weight').textContent = `${(this.currentCorrect.weight / 10).toFixed(1)} kg`;
// 公式高画質イラストをバインド
const spriteUrl = this.currentCorrect.sprites.other['official-artwork'].front_default
|| this.currentCorrect.sprites.front_default;
// ロード完了時に描画
const tempImg = new Image();
tempImg.src = spriteUrl;
tempImg.onload = () => {
imgEl.src = spriteUrl;
// 選択肢ボタンの動的生成
const optContainer = document.getElementById('quiz-options');
optContainer.innerHTML = '';
optionNames.forEach((name, idx) => {
const btn = document.createElement('button');
btn.className = "quiz-opt-btn bg-slate-900 hover:bg-slate-800 p-2.5 md:p-3 rounded-xl font-bold text-xs md:text-sm text-left truncate flex items-center gap-1.5 border-2 border-slate-800 active:scale-95 transition-transform";
const colorBadge = ['red', 'blue', 'yellow', 'green'][idx];
btn.innerHTML = `<span class="bg-${colorBadge}-500 text-white w-5 h-5 rounded-full inline-flex items-center justify-center text-xs font-black">${idx+1}</span> <span class="opt-text">${name}</span>`;
btn.onclick = () => this.checkAnswer(name, btn);
optContainer.appendChild(btn);
});
this.showState('content');
};
} catch (err) {
console.error("Quiz Fetch Failed", err);
this.showState('error');
}
},
checkAnswer: function(selectedName, clickedBtn) {
const imgEl = document.getElementById('quiz-pokemon-img');
const feedbackEl = document.getElementById('quiz-feedback');
const feedbackText = document.getElementById('feedback-text');
// 他の全ボタンを操作不可&透明化
document.querySelectorAll('.quiz-opt-btn').forEach(btn => {
btn.classList.add('pointer-events-none', 'opacity-50');
});
// シルエット(黒塗り)を解除
imgEl.classList.remove('silhouette');
feedbackEl.classList.remove('hidden');
const isCorrect = selectedName === this.currentCorrect.nameJa;
if (isCorrect) {
this.score += 10;
document.getElementById('score-val').textContent = this.score;
feedbackEl.className = "mt-3 p-3 rounded-xl font-bold text-sm bg-emerald-500/30 text-emerald-300 border-emerald-500 border-2 text-center";
feedbackText.innerHTML = `<i class="fa-solid fa-circle-check text-lg mr-1 text-emerald-400"></i> せいかい! +10点!<br>あたまのなかで「${this.currentCorrect.nameJa}」をよんだよ!`;
clickedBtn.className = "quiz-opt-btn bg-emerald-950 border-emerald-500 p-2.5 md:p-3 rounded-xl font-bold text-xs md:text-sm text-left truncate flex items-center gap-1.5 border-2 text-emerald-300";
clickedBtn.classList.remove('opacity-50');
playSystemSound(880, 'sine', 0.2);
} else {
feedbackEl.className = "mt-3 p-3 rounded-xl font-bold text-sm bg-red-500/30 text-red-300 border-red-500 border-2 text-center";
feedbackText.innerHTML = `<i class="fa-solid fa-circle-xmark text-lg mr-1 text-red-400"></i> ざんねん!ちがうよ!<br>せいかいは <strong>${this.currentCorrect.nameJa}</strong> でした!`;
clickedBtn.className = "quiz-opt-btn bg-red-950 border-red-500 p-2.5 md:p-3 rounded-xl font-bold text-xs md:text-sm text-left truncate flex items-center gap-1.5 border-2 text-red-300";
clickedBtn.classList.remove('opacity-50');
// 正解ボタンをハイライト
document.querySelectorAll('.quiz-opt-btn').forEach(btn => {
if (btn.querySelector('.opt-text').textContent === this.currentCorrect.nameJa) {
btn.className = "quiz-opt-btn bg-emerald-950 border-emerald-500 p-2.5 md:p-3 rounded-xl font-bold text-xs md:text-sm text-left truncate flex items-center gap-1.5 border-2 text-emerald-300";
btn.classList.remove('opacity-50');
}
});
playSystemSound(220, 'sawtooth', 0.25);
}
// ポケモン名読み上げ(音声)
speakPokemonName(this.currentCorrect.nameJa);
}
};
function speakPokemonName(name) {
if (speakEnabled && 'speechSynthesis' in window) {
window.speechSynthesis.cancel();
const u = new SpeechSynthesisUtterance(name);
u.lang = 'ja-JP';
u.rate = 1.05;
window.speechSynthesis.speak(u);
}
}
// クイズ初期化
QuizGame.init();
</script>
</body>
</html>
ポケモンのクイズです!