魔球BATTERのボールは、Three.jsもWebGLも使っていない。すべてCSSで描いている。最初はそれが「妥協」だと思っていたが、実装を進めるうちに「CSSでここまでできるのか」という発見の連続になった。
なぜCSSを選んだか
Three.jsを検討した時期はある(Article 19に詳しく書いた)。試してみて断念した一番の理由は、初期ロードの重さだった。バンドルサイズが一気に膨らみ、スマートフォンでの初回描画に時間がかかりすぎた。ゲームとして「すぐ遊べる」ことを優先した結果、CSS + JavaScriptの組み合わせを選んだ。
魔球タグと色の対応
ボールの外見はCSSのradial-gradientで作る。魔球の種類(タグ)ごとに色を変えることで、プレイヤーは「見た目で球種を予測できる」ようになる——ただし意図的に少しだけ曖昧にしてある。
DISAPPEAR(消える): 深紫〜紫
STOP_AND_GO(停止): 深紅〜赤
SKY_DROP(空から落下): スカイブルー〜白
UNDERGROUND_EMERGE(地下出現): 茶〜土色
LOOP(ループ): 深緑〜エメラルド
ZIGZAG / SPIRAL系: インディゴ〜青
色だけで球種を完全に特定できないよう調整している。これはプレイヤーが「目で読む」楽しさを損なわないための意図的な設計だ。
radial-gradientで球体を作る
平面のdiv要素に球体の質感を与えるのに、CSSのradial-gradientが効く。中心を明るく、外側を暗くするだけで「丸い物体」に見える。
/* 紫の魔球(DISAPPEAR系)の例 */
background: radial-gradient(
circle at 35% 35%,
#c084fc, /* 明るい紫(光の当たる面) */
#7e22ce, /* 中間 */
#3b0764 /* 暗い紫(影の面) */
);
光の当たる位置を35%/35%(左上)に設定することで、上から光が当たっているような立体感が出る。これだけで「ただの丸」から「球体」に変わる。
box-shadowで発光(グローエフェクト)
ストライクゾーンにボールが入ると黄色く光る演出がある。この「ヒット判定の視覚フィードバック」はbox-shadowの多重がけで作っている。
/* ゾーン内の発光エフェクト */
box-shadow:
0 0 8px #facc15, /* 内側の鋭いグロー */
0 0 20px #fbbf24, /* 中間のやわらかいグロー */
0 0 40px #f59e0b, /* 外側の広がるグロー */
0 0 80px #d97706; /* 遠くへの拡散 */
4層に重ねることで、単一のshadowでは出せないリッチな発光になる。「黄色く光ったらタップ」という反射的な判断をプレイヤーに促す、ゲームプレイ上重要な視覚信号だ。
座標系とCSSのtransform
ゲーム内の座標は0〜100の相対値で管理している。ピッチャー側(0)からバッター側(100)に向かうprogressと、横方向curve_x・縦方向curve_yの組み合わせでボールの位置を決める。
/* progress: 0→1 でピッチャーからバッターへ */
const x = baseX + curve_x * Math.sin(progress * Math.PI);
const y = baseY + curve_y * Math.sin(progress * Math.PI);
const scale = 0.4 + effectiveProgress * 1.3; /* 近づくほど大きくなる(最大スケール1.7) */
/* CSSへの適用 */
ball.style.left = x + '%';
ball.style.bottom = y + '%';
ball.style.transform = `scale(${scale})`;
scaleを変えることでボールが「近づいてくる」ように見える。Three.jsを使わずに擬似的な遠近感を出せるのは、この単純な計算のおかげだ。
停止・消滅のアニメーション
STOP_AND_GOは途中でボールがスローモーションになる魔球だ。これはJavaScriptで`effectiveProgress`を0.1倍に減速するロジックで実現している。停止区間に入ったら進行速度を1/10に落とすことで、見た目上「止まっているように見える」スローモーション効果を作り出している。
/* 停止タイミングの制御(概略) */
if (progress > 0.4 && progress < 0.6) {
effectiveProgress = baseProgress + (progress - 0.4) * 0.1;
} else {
effectiveProgress = progress;
}
CSSのプロパティではなく、JavaScriptが「どのくらい遅くするか」を計算して進行速度を制御する。アニメーションの制御をJavaScript側で一元管理できる設計だ。
結果として学んだこと
CSSには表現の幅がある。three.jsの豊かさには当然及ばないが、ゲームUI程度の視覚表現なら追加ライブラリなしで十分に作れる。初期ロードを犠牲にせず、ブラウザネイティブな技術で仕上げられたことは、「すぐ遊べるゲーム」の目標に正直に答えられたと思っている。