魔球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程度の視覚表現なら追加ライブラリなしで十分に作れる。初期ロードを犠牲にせず、ブラウザネイティブな技術で仕上げられたことは、「すぐ遊べるゲーム」の目標に正直に答えられたと思っている。