第1章
このデモとは
「テトリスを“本物のブロック”みたいにツヤツヤに描くと、どうなるの?」に、動くコードで答えてくれるサンプルです。
このデモは、Babylon.js ファミリーの WebGPU 専用ランタイム Babylon Lite の公式リポジトリに含まれる、3D テトリスです。ルールはおなじみのテトリスそのものですが、盤面のブロックは平べったい四角ではなく、角に丸みや面取りのついた立体キューブ。写真のような環境の映り込み(HDR)と、ツヤを強調するライティングで、まるでプラスチックの玩具のように光ります。ブロックの見た目は3スタイルを切り替えられ、そのうち1つはかわいい動物「キューブペット」です。
プログラムとしての見どころは、次の3点です。
ただの立方体では“開発中の仮モデル”に見えてしまう。角を丸めたり(フィレット)面取りしたり(ベベル)するメッシュを、コードで生成しています。
PBR 質感+HDR 環境光の映り込み+ACES トーンマッピング。さらに解像度を上げて描いて縮小する“超サンプリング”で、輪郭をなめらかに。
ブロックを1個ずつ描かず、色ごとにまとめて GPU へ。盤面が埋まっても描画命令はほぼ一定。thin instancing の教科書です。
第2章
あそびかた
コードを読む前に、まず遊んでみるのがいちばんの近道です。操作はふつうのテトリスと同じです。
babylonjs.com のライブデモを開く →(WebGPU 対応ブラウザが必要です)
| 操作 | すること |
|---|---|
| ← → | 左右に移動(押しっぱなしで連続移動。後述の DAS) |
| ↑ / X | 回転 |
| ↓ | ソフトドロップ(少し速く落とす/1マスにつき +1点) |
| Space | ハードドロップ(一気に着地/落ちたマス ×2点) |
| P | ポーズ |
| 画面のボタン | 見た目スタイル(Pets/Arcade/Smooth)とミュートの切り替え |
いわゆる公式ガイドライン(SRS 回転・ホールド・壁蹴り・ロック遅延)はあえて入れていません。回転は4状態を素直に切り替えるだけ、着地したら即固定。「小さく・依存なしで・読んで分かる」ことを優先した、潔い実装です。
第3章
用語ミニ辞典
この先の章で出てくることばを先にまとめて紹介します。分からなくなったらここに戻ってきてください。
4つのマスでできたブロックのこと。I・O・T・S・Z・J・L の7種類があります。
次に出るブロックの決め方。7種を1つずつ袋に入れて引くので、7回に1回は必ず全種が出る=極端な偏りが起きません。
左右キーを押しっぱなしにしたとき、最初だけ少し待ってから連続移動が始まるしくみ。押した瞬間に暴走しないための定番です。
いま操作中のブロックが「このまま落ちるとどこに着地するか」を、半透明で先に見せる影のこと。
ソフト=少し速く落とす、ハード=一気に着地させる。どちらもボーナス点がつきます。
金属・プラスチックなど素材の反射を物理法則にならって計算する描き方。ザラつき(ラフネス)を変えるとツヤ感が変わります。
周囲360°の明るい風景(HDR画像)を光源として使い、ブロック表面にうっすら風景を映り込ませる手法。写真のような立体感が出ます。
まぶしすぎる明るさを、目になじむ範囲へ上手に圧縮する変換。映画でも使われる ACES 方式を採用しています。
同じ形のメッシュを、位置だけ変えて何百個も一括描画する GPU の機能。1個ずつ描くよりずっと高速です。
ベベル=角を平らに削る「面取り」、フィレット=角を丸める「丸め」。どちらも輪郭にツヤの線を走らせ、実物らしさを出します。
実際より高い解像度で描いてから縮小し、輪郭のギザギザを消す方法。このデモは解像度を2倍にして使います。
明るい部分がふわっとにじんで光って見える後処理。発光(エミッシブ)と組み合わせてブロックに光の暈(かさ)を足します。
第4章
全体の地図
エントリの tetris.ts は、エンジンとシーン・HDR 環境光・入力を用意して、あとは4つの部品に仕事を渡します。実装のほとんどは 3D 描画担当の renderer.ts に集まっています。
flowchart TD
ENTRY["tetris.ts
エンジン・シーン・HDR環境光・入力"]
subgraph CORE["ゲームと表示"]
direction LR
GAME["game
ルールと盤面(10×20)"]
REND["renderer
3D描画(最大の部品)"]
HUD["hud
スコア等のDOM表示"]
SND["sound
効果音の合成"]
end
subgraph DATA["形と素材"]
direction LR
PIECE["pieces
7種の形と色"]
ROUND["rounded-box
丸めキューブ"]
CHAM["chamfered-box
面取りキューブ"]
PART["particles
破片エフェクト"]
end
ENTRY --> CORE
REND --> DATA
GAME --> PIECE
矢印は「上が下を利用する」向きです。ルール(game.ts)は「どのマスに何色のブロックがあるか」という数字だけを扱い、見た目は一切知りません。その数字を受け取って、丸みや面取りのキューブ・光・映り込み・破片へと“翻訳”するのが renderer.ts の仕事です。ルールと見た目がきれいに分かれているのが、このデモを読むときの最初の目印になります。
おすすめは、①pieces.ts(7種の形)→ ②game.ts(ルール)→ ③rounded-box.ts(キューブの成形)→ ④renderer.ts。小さな部品から読むと、巨大なレンダラが「知ってる部品の組み立て」に見えてきます。
第5章
起動の流れ
ページを開いてから盤面が現れるまで、tetris.ts は次の順で準備を進めます。
flowchart TD
A["① canvas 取得・読み込み進捗(約1MB)"] --> B["② エンジンとシーンを用意"]
B --> C["③ HDR 環境を読み込み
IBL の光源に。ACES トーンマッピングを適用"]
C --> D["④ 解像度を2倍(=4倍密度)に設定
既定の4×MSAAと合わせて実質≒16×アンチエイリアス"]
D --> E["⑤ game・renderer・hud・sound を生成"]
E --> F["⑥ 入力(DAS付き)を接続してループ開始"]
ポイントは④です。画面のドット密度(DPR)をわざと2倍に偽装して、実際の4倍の細かさで描き、最後に縮小します。これに描画側の 4×MSAA が乗るので、合わせて実質およそ16倍のアンチエイリアス。ツヤのあるキューブの輪郭が、ジャギー(ギザギザ)なしでなめらかに見えるのはこのおかげです。ただし DPR の上書きを拒むブラウザもあるため、その操作は try / catch で包み、失敗しても止まらないようにしています。
読み込む約1MBの中身は、映り込みに使う HDR 環境と、PBR の反射計算を速くするための BRDF ルックアップ表です。画像アセットはこれくらいで、ブロックやUIの絵は持っていません。
第6章
盤面とルール
担当:game.ts。盤面は横10×縦20マス。数字だけで状態を持ち、見た目には関与しません。
落下スピードはレベルで速くなる
1マス落ちるまでの時間は、レベルが上がるほど短くなります。式は base = 1000 × 0.82^(level−1)(ミリ秒)で、下限は 80ms。レベル1で約1秒、上げていくと最速 80ms までキビキビ落ちます。ラインを10消すごとにレベルが1上がります(level = 1 + floor(消したライン数 / 10))。
| レベル | 1マス落下の間隔(目安) |
|---|---|
| 1 | 1000 ms |
| 3 | 約 672 ms |
| 5 | 約 452 ms |
| 8 | 約 249 ms |
| 12 | 約 107 ms |
| 14+ | 80 ms(下限) |
スコア
| できごと | 点数 |
|---|---|
| 1ライン消し | 100 × レベル |
| 2ライン消し | 300 × レベル |
| 3ライン消し | 500 × レベル |
| 4ライン消し(テトリス) | 800 × レベル |
| ソフトドロップ | +1 / 1マス |
| ハードドロップ | +2 / 落ちた1マス |
ブロックは着地するとすぐ固定されます(ロック遅延なし)。固定した瞬間、横1列がすべて埋まっていれば消去。消える前にそのマスの色を控えておき(破片エフェクト用)、上の段を下へ詰めます。新しいブロックは SPAWN_COL = 3 の位置・最上段に出現し、そこがすでに埋まっていればゲームオーバーです。
第7章
7つのテトリミノ
担当:pieces.ts。7種類の形を、計算ではなく手書きの座標表で持っています。
各ピースは「4つの回転状態」を、それぞれ枠内のマス目 [列, 行] の並びとして直接書き下しています(I は 4×4、他は 3×3 や 2×2 の枠)。SRS のような回転計算を使わないのは、「デモを小さく・依存なしで・目で追える」ことを優先したからだとコメントに明記されています。壁蹴り(キック)も入れていません。
| ピース | 動物 | 色 |
|---|---|---|
| I | ぶた | ピンク |
| O | パンダ | 白グレー |
| T | うさぎ | タン(薄茶) |
| S | かに | レッド |
| Z | ひよこ | イエロー |
| J | ねこ | スレートブルー |
| L | いもむし | グリーン |
次に出るピースは game.ts 側の 7-bag(7種を袋に入れて引く方式)で決まるので、同じピースばかり続く・全然来ない、といった偏りが起きません。ホールド機能はありません。
第8章
キューブを成形する
担当:rounded-box.ts と chamfered-box.ts。このデモが「ツヤの手ざわり」に最もこだわった部分です。
コメントいわく——完璧な90°の立方体は「開発中の仮モデル」に見えてしまう。現実の成形プラスチックの部品は、必ずどの辺にもわずかな丸みや面取りがあり、それが輪郭に沿ってツヤの筋(スペキュラハイライト)を一周させます。だから2種類のキューブを、頂点・法線・UVごとコードで生成しています。
6枚の面+12本の辺を1/4円柱で+8つの角を球の1/8で埋める。分割数 seg=3 で約250頂点。柔らかく光が回る“成形プラスチック”の質感に。
すべての辺と角を45°でカット。96頂点・132インデックスと軽量で、フラットシェーディング。輪郭にシャープなツヤの筋が出る“アーケード”な質感に。
どちらも1個あたりの頂点数を抑えてあるので、後述の thin instancing で盤面いっぱい(最大およそ204個)に並べても軽快です。
第9章
一気に描くしくみ
担当:renderer.ts(692行・最大のファイル)。3D テトリスの心臓部で、ブロックを“1個ずつ”描かない工夫の塊です。
盤面には最大200個のブロック+操作中4個+ゴースト4個が並びます。これを素直にメッシュ200個で描くと重い。そこで色ごとに「1つのメッシュ」を用意し、位置だけ違うコピーを大量に生やす thin instancing を使います。7色ぶん+ゴースト用の型があり、毎フレーム盤面を見て「どの色をどこに何個置くか」の行列表だけを更新します。
// 色ごとに固定枠を確保。使わない枠は「つぶれた行列(スケール0)」で不可視に MAX_INSTANCES = BOARD_COLS * BOARD_ROWS + 4 // 10*20 + 4 // 描画命令は一度だけ記録して使い回す: drawIndexed(_, MAX) // バンドルに1回焼き、以後は記録し直さない // 中身(行列)は毎フレーム GPU へ直接転送: device.queue.writeBuffer(...)
使わない枠はスケール0の「つぶれた行列」を入れておくと、GPU 側では“見えないゴミ”として無視されます。だから盤面が空でも満杯でも、記録する描画命令はいつも同じ1回ぶんだけ。命令を毎フレーム作り直さず、中身の数字(行列)だけを流し込むので、CPU の負担がとても軽いのです。盤面を囲う石枠も同じ発想で、丸めキューブを66個リング状に並べて作っています(左右の列はZ軸まわりに90°回して、丸い辺が外を向くように)。
盤面200+操作中4+ゴースト4。上限を MAX_INSTANCES で固定してあるので、描画のコストが「今どれだけ積まれているか」に左右されません。ゲームが白熱しても処理が重くならない、予測しやすい設計です。
第10章
光と質感
ブロックが“玩具のプラスチック”に見えるのは、質感(PBR)と光の合わせ技です。担当は renderer.ts の中。
基本は PBR。表面のザラつき=ラフネスを小さく(約0.22)すると、キリッとしたツヤの点が出ます。さらに各ブロックは自分の色をほんの少し(約35%)自分から発光させ、後処理のブルームで光の暈(かさ)をまといます。映り込みには HDR 環境(IBL)を使い、視線方向に沿って環境を「ラフネスの分だけぼかして」サンプリング。背景も同じ環境をぼかして敷くので、写真スタジオのようなやわらかい奥行きが出ます。
| 光・素材 | 設定のねらい |
|---|---|
| ディレクショナル(主光源) | カメラの少し後ろ上に置き、ツヤがまっすぐ手前へ跳ね返るように |
| ヘミスフェリック(環境光) | 上方向 [0,1,0.25]・強さ0.75。床面のもち上げ(暗く沈ませない) |
| 背面パネル | ほぼ黒(0.018,0.02,0.028)・中低ラフネス0.42。ブロックを背景から浮かせる |
| 光沢ブロック(Arcade/Smooth) | 低ラフネス0.22でシャープなツヤ+発光35% |
| Pets ブロック | マットな玩具肌(ラフネス0.62)+強め直射3.0。暗い動物色を暗い舞台で沈ませない |
| ゴースト | 半透明(アルファ0.3)のグレー。着地位置のプレビュー |
カメラは弧を描いて回るアークローテート式で、行き過ぎないよう範囲を制限しています(半径24〜42、上下は π×0.32〜0.62、左右は正面 π/2 から±0.45 ラジアン)。真上・真後ろに回り込めないので、ツヤが最も映える角度から大きく外れません。
第11章
3つの見た目
画面のボタンひとつで、ブロックの見た目を3スタイル切り替えられます。使うキューブのメッシュと質感が入れ替わります。
Kenney の CC0 素材「Cube Pets」の動物キューブ。頂点色をあらかじめ焼き込んであり、7色が7匹の動物に対応。マットな玩具肌。
45°の面取りキューブ。シャープなツヤの筋が輪郭に走る、キリッとしたゲームセンター風。
たっぷり丸めたキューブ。やわらかいツヤが一周する、上品でなめらかな質感。
どのスタイルでも、盤面の数字(どこに何色)と描画のしくみ(色ごとのインスタンシング)は共通です。差し替わるのはキューブの形と表面の質感だけ。データと見た目を分けておいた設計が、こういう切り替えを簡単にしています。
第12章
ラインクリアの演出
担当:particles.ts と renderer.ts のカメラシェイク。ラインが消える瞬間の“気持ちよさ”を作ります。
横1列が消えると、そのマスたちが元の色のまま小さなキューブになって砕け散ります。粒は盤面のほんの手前(z=0.46)から湧いて、こちらへ飛んできます。重力22で落ち、上向き初速3〜7・手前向き2.5〜6.5・軸まわりの回転±9でくるくる回転。寿命0.85〜1.4秒のあいだに、大きさがt²でスッと小さく消えます。粒はすべて1枚の「陰影なし・頂点色」シェーダ(明るさ2.4)で描かれ、負荷を抑えています。
ラインを消すたびにカメラが小さく揺れます。揺れは周波数の違う2つの正弦波(38Hz と 31Hz)を直交方向に重ねて、機械的でない自然なブレに。揺れ幅は消したライン数に比例し、4ライン同時消し(テトリス)がいちばん大きな“ドン”になります。
第13章
表示と音
担当:hud.ts(画面表示)と sound.ts(音)。どちらも「3D の描画を汚さない」ための外付け担当です。
HUD は全部ふつうの HTML
スコア・ライン・レベル、次のピースの4×4プレビュー、操作ヘルプ、スタイル/ミュートの切り替えボタン——これらは canvas の上に重ねたただの DOMです。3D の盤面に文字を描かせずに済むうえ、すりガラス風(backdrop-filter: blur(8px))の見た目も CSS だけで作れます。ゲームオーバーとポーズの半透明オーバーレイもここが出します。
音は1つ残らず合成
効果音は音声ファイルを持たず、WebAudio のオシレータとエンベロープでその場で合成します。移動・回転・ソフト/ハードドロップ・固定・ライン消し・テトリス・レベルアップ・ゲームオーバー・ポーズ——それぞれが短い「発振音の重ね(チップチューン風のピコッ・シュン・アルペジオ)」。プツッというノイズが出ないよう、音量を指数カーブでなめらかに立ち上げ・下げしています。AudioContext はブラウザの自動再生規制に合わせ、最初のキー操作の瞬間に初めて作られます。BGM はありません。
第14章
3つの設計テーマ
約2,600行を貫くのは、この3つの考え方です。自分のプロジェクトにもそのまま持ち帰れます。
回転は SRS を使わず4状態の座標表、乱数は7-bag、音も画像も外部素材に頼らない。読めば全部わかる“監査しやすさ”を優先。
色ごとに1メッシュのインスタンシング、描画命令は1回だけ記録、行列は毎フレーム直接転送。命令数を状況に左右させない。
ただの立方体を避け、丸め・面取りで輪郭にツヤを走らせる。PBR+HDR+約16×AA+ブルームで“成形プラスチック”に。
このコードのコメントには「なぜそうしたか」がていねいに書かれています。完璧な立方体は仮モデルに見える/SRS を入れないのは小さく保つため/未使用枠はスケール0で不可視に——判断の理由ごと読めるのが、このサンプルの教材価値です。Minecraft・Platformer デモと共通する“コメント文化”が、ここにも息づいています。
第15章
ファイル一覧(全9ファイル)
コードリーディングのおともに。行数は master ブランチのソース実測値です。renderer.ts が全体の約4分の1を占めます。
| ファイル | 行数 | やくわり |
|---|---|---|
| エントリ | ||
tetris.ts | 267 | エンジン・シーン・HDR環境・約16×AA・入力(DAS)を用意して各部品へ委譲 |
| ゲームロジック層 | ||
game.ts | 380 | ルールと盤面(10×20)。落下・回転・ライン消去・スコア・7-bag・ゲームオーバー |
pieces.ts | 228 | 7種テトリミノの形(4回転ぶんの座標表)と色・動物の対応 |
| 描画・形状層 | ||
renderer.ts | 692 | 3D描画の心臓部。インスタンシング・PBR・IBL・ライト・カメラ・石枠・シェイク |
rounded-box.ts | 179 | 丸めキューブのメッシュを生成(面+辺の1/4円柱+角の球1/8) |
chamfered-box.ts | 137 | 面取りキューブのメッシュを生成(45°ベベル・96頂点) |
particles.ts | 205 | ライン消去時に飛び散る小キューブの破片エフェクト |
| UI・音層 | ||
hud.ts | 299 | スコア・次ピース・操作ヘルプ・切替ボタン・各オーバーレイ(すべてDOM) |
sound.ts | 186 | 効果音をWebAudioで合成(移動・回転・固定・ライン消し・テトリス等) |
行数は master ブランチ(2026年時点)のソース実測値です。合計およそ2,573行。