0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

バイブテトリスコーディングしてみた。Svelte + Vite でネオン落ちゲーを組むまで

0
Last updated at Posted at 2025-12-12

「ネオンでギラギラ光るテトリス作りたいな〜」という思いつきが先で、技術選定は後から考えるパターン。結局 Svelte + Vite で組んで、光らせて、鳴らして、途中で何回か笑いながら完成させた。そのときの記録。

完成したもの

普通に遊べるテトリス。移動と回転、ソフトドロップにハードドロップ。ここまでは教科書通り。
https://my-tetris-mu.vercel.app/

ただ、せっかくなので 7-Bag ランダマイザを入れた。これがないと「I ピース全然来ないんだけど!?」という悲鳴が発生するので。あと壁蹴り(SRS 風)も実装して、壁際でもちゃんと回転できるようにした。

見た目はネオングロー全開。レベルアップと TETRIS 達成時にギラッと光る。音も Web Audio API で自作して、Classic(ピコピコ)・Cyber(ジャキジャキ)・Mellow(ぽよぽよ)の 3 パターンから選べる。スマホでも遊べるようにタッチ用のコントロールパッドもつけた。

7-Bag ってなに

テトリス初心者向けに説明すると、7-Bag は「7 種類のブロックを袋に入れてシャッフルして、1 袋分を全部出し切ってから次の袋を開ける」という仕組み。見た目はランダムなんだけど、最悪でも 13 手以内には欲しいピースが来る。「I ピースが 40 手来ない」みたいな地獄を防げる。

実装は logic.jsfillPieceQueue で、やってることは素朴なシャッフルだけ。でも 7 個単位で補充するルールを守ってるから、ちゃんと機能する。

SRS(壁蹴り)ってなに

Super Rotation System の略。公式テトリスで使われてる回転ルールで、壁や床に当たっても「ちょっとずらして回る」という親切設計。

今回は簡易版で、config.jsKICK_OFFSETS として [ [0,0], [-1,0], [1,0], [0,-1], [-1,-1] ] を定義してる。本家ほど複雑じゃないけど、壁際で回れなくてイライラする問題は解消できた。

開発中にハマったこと・笑ったこと

シークレットモードで裸のテトリス

開発中、シークレットモードで開いたら一瞬スタイルが崩壊する現象に遭遇。スタートボタン押すと直るんだけど、最初の一瞬がひどい。

原因は Tailwind CDN。シークレットモードだとキャッシュが効かないし、サードパーティへの接続も抑制されがち。結果、CSS が届く前に素の HTML が一瞬見えてしまう。

直し方は単純で、Tailwind をビルドに組み込んだ。app.css@tailwind base/components/utilities 書いて、CDN への依存を消した。これで初回表示から安定。

Analytics のコピペで爆死

Vercel Analytics を入れようとして、Next.js のサンプルをそのまま持ってきた。import { Analytics } from "@vercel/analytics/next" って書いて、当然動かない。Svelte なのに。

正解は @vercel/analytics から inject() を呼ぶだけ。main.js に 1 行追加して終わり。フレームワーク専用のインポートパスには気をつけような、という教訓。

pnpm のアップデートが効かない

pnpm self-update を実行しても何も起きない。しばらく悩んで、「あ、asdf で管理してるんだった」と気づく。

asdf 経由だとこうなる:

asdf plugin update pnpm && asdf install pnpm 10.25.0 && asdf set pnpm 10.25.0 && asdf reshim pnpm

シェルを開き直して PATH をリフレッシュすれば完了。自分の環境くらい覚えておけという話。

OG 画像が Vite のアイコン

Twitter でシェアしたら、OG 画像が Vite のロゴだった。「チュートリアル始めたの?」みたいな空気になって恥ずかしい。

public/og-image.svg を自作して差し替えた。ネオンブロックとタイトルを入れた 1200x630 の画像。これでようやくテトリスっぽくなった。

デバッグ用のテキストがうるさい

config.js を見返したら「🛠️ FEATURE CHECK」とか「💥 TRIGGER SHAKE」とか書いてある。深夜テンションで書いたんだろうな。まあ、どこで何が起きてるか分かりやすいからいいか。

コードのちょっとした見どころ

7-Bag の実装

export function fillPieceQueue(queue) {
    const pieces = 'TJLOSZI';
    while (queue.length < 7) {
        const bag = pieces.split('').sort(() => Math.random() - 0.5);
        for (let type of bag) queue.push(getPieceMatrix(type));
    }
    return queue;
}

10 行もない。でも「1 袋 7 個を出し切る」というルールを守ってるから、ちゃんと偏りが抑えられる。アルゴリズム的には何も難しいことしてない。

音のパラメータ

効果音は Web Audio API で全部 JS で書いてる。SOUND_PATTERNS に波形と周波数を定義してあって、Classic は四角波でピコピコ、Cyber はノコギリ波でジャキジャキ、Mellow はサイン波でぽよぽよ。

パラメータが全部コードなので、「もうちょっと高い音にしたいな」と思ったら数字をいじるだけ。耳で聞きながら調整できるのが楽しかった。

UI まわりの話

スマホ対応はタッチ用の ControlPad を作って対応した。親指で押しやすいサイズを意識。PC でも出るけど、まあ邪魔にはならない。

レベルアップと TETRIS 達成時の光るやつは、CSS のテキストシャドウとアニメーションだけで実装。重い処理は何もしてない。「光れば楽しいでしょ」という雑な発想だけど、実際楽しいからヨシ。

あと、メニュー画面で Space キー押すとゲームが始まる。油断してると急に始まる。仕様です。

動かし方

pnpm install
pnpm run dev -- --open   # 開発サーバー起動
pnpm run build           # 本番ビルド
pnpm run preview         # ビルド結果の確認

Vercel にデプロイするなら、リポジトリ連携するだけ。Vite を自動検出してくれる。Analytics はダッシュボードで ON にすれば inject() が動く。

学んだこと

CDN 依存は危険。 シークレットモードとか、ネットワークが不安定な環境で露呈する。最初からビルドに組み込んでおくのが無難。

フレームワーク専用のサンプルコードはそのまま使えない。 Next.js 用、Svelte 用、React 用……ちゃんと確認しないと動かない。当たり前だけど、急いでるとやりがち。

ロジックと UI を分けると楽。 logic.js にゲームロジック、config.js に設定値を寄せたおかげで、見通しがよくなった。テストも書きやすい(書いてないけど)。

Web Audio API は思ったより簡単で楽しい。 波形と周波数をいじって音を作るのは、地味に時間を忘れる作業だった。

連絡先

バグ報告や感想があれば:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?