Zipanguelike

『Zipanguelike』のつくりかた、または地図タイルの動的プロシージャル生成

Zipanguelikeの画面

クライアントサイド地図タイルサーバーの応用実践『Zipanguelike』。プロシージャル生成技術により、メッシュデータの粒度を超える地形を表示します。

目次

  1. ローグライク風ドット絵日本地図
  2. 原理
  3. 基本の定義
  4. ズーム時の挙動
  5. 座標変換
  6. メッシュデータの粒度
  7. WebGL2.0の制約
  8. 地図タイル生成のからくり
  9. さらに作りこむ
  10. 地形のプロシージャル生成
  11. 町のプロシージャル生成は難しい
  12. 乱数の一貫性・再現性に注意
  13. 小さなままのマップチップ
  14. 自作マップチップ表示機能
  15. おわりに
  16. 用語・注釈
  17. 補遺

ローグライク風ドット絵日本地図

『はんけトケ』の記事「クライアントサイド地図タイルサーバー」の技術を全面的に用いて、『Zipanguelike』なる地図ウェブアプリを作ってみました(このサイトのホーム)。この記事の主題なので、まずは一度触ってみてください。

おわかりのとおり、日本地図をローグライクつまりレトロRPG風のドット絵で表示するものです。ありふれた陳腐なネタではあるものの、案外というかそれゆえにというべきか、正面からまともに取り組んだ実装は見たことがありません。既存の地図アプリにそれっぽいスタイルを適用したものがせいぜいです。実用性も収益性もなさそうなネタを、わざわざコストをかけて一から開発・運営する酔狂はいなかったわけですね。

しかし、地図タイルをクライアント側で生成することにすれば、最も心配なサーバー関連のコストはかかりません。機能豊富なウェブ地図ライブラリーを無料で利用でき、十分な範囲と粒度のオープン地理データが出そろっており、複雑な処理を瞬時にこなすGPUもあらゆる機器に備わっている。かつての「酔狂」を「当たり前」にする機は、今や熟しているのです。

原理

用語が紛らわしいので、ここではとりあえず次のように呼んで区別することにします。

はんけトケの記事では、人口メッシュの内容を色分けした地図タイルをサービスワーカーで生成し、ヒートマップとして表示していました。それと同じ要領で、メッシュを彩色する代わりにマップチップで表す地図タイルを生成すれば、マス目状のドット絵日本地図になるはずです。

原理はとてもシンプルですね。ただし、実装にはこまごました工夫を要します。以下にかいつまんで説明します。

基本の定義

マップチップは16×16ピクセル単位とします。地図タイルは256×256ピクセルのラスター(ビットマップ)画像とします。ひとつの地図タイルにマップチップがぴったり16×16個収まることになり、処理しやすく見た目もすっきりします。

ズーム時の挙動

まず考えねばならないのが、地図をズームしたときのマップチップの見え方です。

さっき「ひとつの地図タイルにマップチップが16×16個収まる」と決めました。しかし、これは奇妙な定義です。ひとつの地図タイルに描かれるべき範囲は、ズームレベルに応じて変化するはずなのですから。

にもかかわらず、常に「ひとつの地図タイルにマップチップが16×16個」であるとはどういうことでしょう。それはすなわち、「マップチップはクラスター的に表示が切り替わる」ということ。具体的な挙動を言葉で説明しようとするとややこしいのですが、Zipanguelikeを触ってみたなら意味は明らかでしょう。

ズームレベルの異なる画面を並べた図
つまりこんな具合に、ズームにしたがってマス目の粒度が増減する。一種のLODともいえる

もしクラスター的でなかったらどうなるか。地図アプリのズームの画角変化は極端です。マップチップを敷き詰めたとして、あるズームレベルでは最適に見えても、ちょっとズーム操作したとたんチップが1ピクセル以下にまで縮小されてしまったり、逆に画面一杯に拡大されてしまったりします。それはそれでいいとの考えもあるでしょうが、わたしの望む挙動ではありません。

なお実際には、地図タイルあたりマップチップは常に16×16個固定ではなく、8×8個や4×4個になることもあります。やや不規則ですが、ズーム操作していてなるべく違和感のないよう調整した結果です。

座標変換

基本の定義にはもうひとつ奇妙な点があります。

地図は国土交通省などの地理情報メッシュデータ(地域メッシュ)を原典とします。が、それらはXYZ形式の地図タイルに合わせた区切りになどなっていません。もしこのメッシュの格子をそのままマップチップにしたら、「ぴったり16×16個収ま」ってくれません。微妙にはみ出したり足りなかったりしてしまいます。

よって、地図タイル側の座標系できっちり整列するようメッシュを調整せねばなりません。具体的な処理としては、まずメッシュの各格子のID(地域メッシュコード)を経緯度に変換し、それをさらにXYZ形式のタイル番号に変換してやります。変換前後の差分は適当に処理します。

地域メッシュコードから経緯度を求めるための計算式は地域メッシュの仕様書(PDF)に、経緯度からタイル番号を求めるための計算式はオープンストリートマップのウィキに詳しく記されています。

メッシュデータの粒度

ところでメッシュの粒度、地域メッシュにおける3次・4次・5次などといった区分はどれを選ぶべきでしょうか。先に結論をいうと、4次としました。

正確には、XYZ形式タイルにおけるズームレベル16の粒度とします。このときのタイル1枚の大きさは約500メートル四方で、おおよそ地域メッシュの4次の格子に相当します。なお原典のデータは必ずしも4次だけとは限らず、もっと細かいデータも適宜変換して使っています。

ではなぜその粒度にしたのかといえば、次に述べる、データを扱う側の都合です。

WebGL2.0の制約

地図タイルの生成(描画)には、WebGL2を利用して高速化を図ります。したがって、データはWebGL2で扱える形式やサイズにしなくてはなりません。

形式はRGBA8UI(RGBA・各8ビット符号なし整数)テクスチャー画像とします。そしてその大きさは、4,096×4,096ピクセルを上限とします。

WebGL2が扱えるテクスチャー形式は仕様上は多岐にわたるものの、実際には環境・実装にかなり左右されます。任意の形式が表面的には受け入れられたように見えても、あまり信用できません。その形式にネイティブに対応しておらず(それでもエラーにならないのが曲者)、float型が内部で整数化されてしまったり、節約のつもりで輝度のみのグレースケールにしてもRGBAに拡張されてしまったりすることがあるのです。どの環境でも確実に使え、かつ無駄のないテクスチャーはRGBA8UIあたりと考えておくのが無難なようです。

4,096×4,096ピクセルの上限も同様の配慮によるもので、このサイズはWebGL2で最低限扱えることが保証されています。いまどきそんな最低要件に無理に合わせる必要があるのかといえば、大ありです。iOS版Safariがまさにその水準の制約をいまだに課しているので。最近の機種では多少緩和されているかもしれませんが(?)、古い機種を使っている人もまだたくさんいるでしょう。毎度足を引っ張るiOS版Safariですが、前向きに考えれば、これで動くよう作っておけば他のほとんどの環境でも動きそうです。

さて、日本全域をおおよそ3千キロメートル四方とすると、4,096×4,096ピクセルにすっぽり収まるのは3次メッシュ(1キロ四方格子)ということになります。しかし、1つの格子の情報量を1バイトとしてRGBAのそれぞれに格納すれば、その2×2倍の粒度、すなわち4次メッシュが収まります。データの粒度を4次メッシュ相当としたのはこれが理由です。

グレースケールのピクセルで表したメッシュデータ
輝度テクスチャー化したメッシュデータ。これを4ピクセルずつRGBAに振り分けたものが最終形

なお実際には、4,096×4,096ピクセルのテクスチャーに、4次メッシュ相当のデータ1つだけでなく、3次メッシュ相当のデータも1つ一緒に詰め込んでいます。平均すると、4次相当メッシュ1格子あたり10ビットの情報量があるわけです。

そこまでしなくても、テクスチャーを複数枚使えばラクに情報量を増やせるではないか。そのとおりなのですが、iOS版Safariはテクスチャーサイズだけでなくサイトに割り当てられる総メモリー量そのものが非常に少なく、MapLibre GL JS等ウェブ地図ライブラリーもリソースを相当消費することを考えると、できるだけ節約するに越したことはありません。それにこのほうが、処理のオーバーヘッドも少なそうです(少なくとも増えはしないはず)。とにかく一般論として、リソースを安易に無駄遣いしていると、将来拡張する余地が減ってしまいます。

地図タイル生成のからくり

地図タイルのリクエスト応答にはサービスワーカーを使います。最大の肝ですが、方法ははんけトケの記事と同じため割愛。そして地図タイルの描画には、述べたとおりWebGL2を使います。

マップチップの描画ごときにWebGLの1ではなく2を選ぶ理由は、2が普及し尽くしており逆に1を選ぶ理由がないからです。……それでは身もフタもないので重要な理由をひとつ挙げると、頂点シェーダー内で入力テクスチャーを参照できるからです(1でもできるがオプション扱い)。

16×16ピクセル単位のマップチップを描画するのに、フラグメントシェーダー側でピクセル毎にいちいち入力テクスチャーつまりメッシュデータを参照して描くべきマップチップを判じるのは、無駄でしかありません。他方、頂点シェーダー側でマップチップを決めてしまえば処理量は圧倒的に少なく済みます。

地図タイル1つにつきマップチップは16×16=256個なので、各チップを4頂点の正方形ポリゴンとすれば1,024回、1頂点のポイントスプライトとすればたった256回の処理で済むのです。もしフラグメントシェーダー側で処理したら、256×256ピクセル=65,536回です。

マップチップをポリゴンとするかポイントスプライトとするかは、それぞれ一長一短あるため目的に応じて決めます。Zipanguelikeでは、始めはポリゴンとして作っていましたが、途中でポイントスプライトに変えました。ポリゴンなら変形など諸々ラクなうえボクセルへの拡張も容易だったりしますが、Zipanguelikeはシンプルな2D表現に徹します。

さらに作りこむ

目指す挙動とデータ構成と地図タイルの生成方法とが決まったので、あとは粛々と実装するだけ。たいへん面倒ではありますが、定番のウェブ技術・GIS技術の組み合わせにすぎず、質的に新奇で困難なことはありません(本当の課題は、わかりやすく使いやすいユーザーインターフェイス開発のほう)。

ただ、ネタというのは徹底しないと面白くありません。せっかく地図タイルを動的に生成しているのだから、4次相当メッシュを超える詳細な地形をプロシージャル生成し、いかにもローグライクらしい「1マス=人間サイズ」にまでズームインできるようにしてみます。

その目標の人間サイズは、約2メートル四方と定めました。4次相当メッシュ約500メートル四方の格子を、切りよく256×256マスに分割した大きさです。

地形のプロシージャル生成

地理に詳しいかたもプログラマーのかたも、「地形は再帰的な自己相似形、つまりフラクタルっぽい」ことはご存じでしょう。再帰処理プログラミングの解説では、よく地形生成のトピックが紹介されます。

わたしが生まれる前の教科書にさえ載っている古典的な知見・手法ですが、Zipanguelikeにおける地形生成は基本的に、それを素直に応用しているだけ。

具体的には、たとえば基となるメッシュ格子が「森林」だとしたら、それを分割した256×256マスもまずすべて森林であるとして、その四辺を自己相似的に揺らぐ周期と振幅でずらすのです。

なんとも安直な処理ですが、結果はZipanguelikeをご覧のとおり、けっこうそれらしい感じになりました。さすが古典だけあり強力な効果ですね。

4次相当メッシュそのままの図と、プロシージャル生成で詳細レベルを上げた図
輪郭をずらすほか、内側もそれらしく手を加えている。人間などのチップも現れる

なおお察しのとおり、このプロシージャル生成処理は頂点シェーダー内で行っています。さすがにCPUには重荷なので。地図タイルの描画にWebGL2を導入し、処理量のより少なく済むポイントスプライトを選んだ最大の理由は、実はこのためでした。もしプロシージャル生成までしないのなら、はんけトケのヒートマップと同じく2DキャンバスにCPUで描く方法でもとくに問題なかったでしょう。

町のプロシージャル生成は難しい

いっぽう家のマップチップで表される建物用地について、これを村や町や都市らしくプロシージャル生成するのは難しく、いろいろ試行錯誤したものの、素朴なアルゴリズムを一様に適用するにとどまっています。

何が難しいかといって、なにしろ面積があまりに広すぎ、何をどう工夫してみても、結局は単調な景色が延々続いてしまうのです。また四角いマップチップを並べる表現上、斜めや曲線的な配置をしにくい、つまり俯瞰ではそれらしく見えてもズームインすると破綻してしまう(見苦しかったり無秩序に見えたりする)ことも解決を難しくしました。

L-systemや都市形態学などの知見、ゲーム開発者会議(GDC)のそれこそ各種ローグライク作者の発表なども参考にはなるでしょうが、一朝一夕にものになるわけもなく、即時応答の動的生成ではそもそも実行できる処理の複雑さに限界があります。よってこの件は割り切り、むしろより単純にして軽さをとる方針としました。

建物を単色で強調した、町のプロシージャル生成結果
ボロノイ図と簡易直交迷路のアルゴリズムが透けて見える、生成した町の様子。人口や地域ごとにパラメーターを変えてみても焼け石に水で、どこまでも同じ調子なのがつらい

乱数の一貫性・再現性に注意

プロシージャル生成の内容は左様に行きあたりばったりで参考にならないかもしれませんが、一般的な注意がひとつあります。処理に用いる疑似乱数についてです。

乱数・ノイズのたぐいをシェーダー内で生成する際、float型を使うと、環境による型精度の差(?)から異なる値が返ることがあります。あるいは経験則として、sinなどの組み込み関数がGPUだかドライバーだかにより微妙に異なる値を返している気もします。あいまいな理解で恐縮ですがいずれにしてもこの現象、特殊効果に使うぶんにはかまいませんが、地形生成においては困りますね。同じ場所の地形が機器によって違ってしまうことになります。

シェーダーの型精度を指定する仕組みは一応あるものの、実際の効果は例によって当てになりません。よって主要な地形生成に関わる疑似乱数の生成には、整数の式やあらかじめ用意したノイズテクスチャー(要は乱数表)を使うことにしました。主要でない部分はこの限りではありませんが、枝葉の齟齬は許容範囲でしょう。

よく知られていることながらついでに触れておくと、JavaScriptの標準乱数には再現性がありません。CPU側処理の乱数にも再現性を求めるなら、適当な疑似乱数式を自前で導入する必要があります。

小さなままのマップチップ

景色の単調さのもうひとつの原因は、マップチップの種類の少なさです。とはいえ単調さを破るほどのチップ数となると膨大で、簡単に用意できるものではありません(チップ自体をプロシージャル生成する手もあるけれど)。

昔ながらの次善策として、パレット替えがあります。Zipanguelikeの内蔵マップチップデータはインデックスカラーで、任意のパレットで描画できる仕組みになっています。地域や緯度(≒気候)ごとに色を変えてみようと企んだのですが、しかしその程度では結局単調さを免れそうにないのと、次に述べるユーザーの自作マップチップを表示できる機能との兼ね合いで見送りました。

自作マップチップ表示機能

ユーザーの自作マップチップ(ユーザータイルセット)で地図を表示するのは、Zipanguelikeにとって重要な機能と考えています。需要の有無はさておき、それが可能なのは地図タイルのクライアント側生成ならではで、つまりは技術的アイデンティティーを象徴するためです。

ただし、マップチップを一般ユーザーに作ってもらうとなると、あまりややこしい仕様にはできません。一般的な画像編集ソフトでは、さまざまな独自属性が含まれるゲーム用テクスチャーどころか、パレット替えを見越したインデックスカラー画像すら作るのは難しいでしょう。よってファイル形式は最もありふれたフルカラーPNGとし、パレット替えの仕様をなくしたのです。

玄人筋には物足りないでしょうが、より多くの人に使ってもらえるよう間口を広げることを優先しました。マップチップの種類を増やしたくない(むしろできるだけ減らしたい)のも、同じ理由からです。

おわりに

説明は以上です。

こんなものの作り方に正解などなく、たとえばベクターデータを解釈してマップチップを配置したっていいし、そもそもマップチップにこだわらなくてもいいわけです。書いておいて何ですが、こうした各論は大して重要ではありません。重要なのは、「クライアントサイド地図タイル生成」や「少ない情報をいかに豊かに見せるか」や「プロシージャル生成による補完」といった考え方をわがものとし、いかに応用するかです。たとえばサーバー配信とこうしたクライアント側生成とを組み合わせれば、配信情報を大幅に減らしたり、よりリッチで楽しい表現ができたりするかもしれません。

もっといえば、というかわたし本来の志向としては、「非伝統的な地図表現」や「あいまい・架空・役に立たない地図」を作る自由、そうした多様な地図を今や「サーバーレスで簡単・安価に公開できる」ことを示したかったのです。そんな文句をただ叫んでみたところでだれも見向きもしないので、実証するネタとしてZipanguelikeを作ってみた次第。果たして成功しているかわかりませんが、自分ではけっこう気に入っています。

町に人間がたくさんいる様子
メッシュデータに基づく人口が、数値や色ではなく本当に人々の多寡で表される
夜の地図で建物の灯りが輝いている様子
「夜間」には建物の灯りで都市が浮かび上がる

用語・注釈

地図タイル,XYZ形式,サービスワーカー
はんけトケの記事「クライアントサイド地図タイルサーバー」を参照。
「ありふれた陳腐なネタ」について
たとえば、アイデアソンで「RPGみたいなドット絵のウェブ地図」なんて案を出してみても箸にも棒にも掛からないだろう。しかし、だからといって作る価値がないわけではない。どんな技術でいかに表現するか、そしてそれを実装するタッチにも創造性は宿るのだから。
マップチップサイズについて
Zipanguelikeではマップチップのサイズを最終的に可変とした(地図タイル側のサイズを変えてつじつまを合わせる)。ただし不具合が生じる余地が増えそうなため無段階とはせず、現状16・12・8ピクセルの3種にのみ対応している。
地図タイルサイズについて
当初は本文のとおり256×256ピクセル(マップチップが16ピクセルの場合)としていたものの、2026年6月現在512×512ピクセルに改めている。Chrome・EdgeなどChromium系ブラウザーに生じる「地図タイルの応答が極端に遅くなる(特にフルスクリーン時)」現象に対処するため。
原因は不明。知る限りこの現象はだいぶ前からあったが、近ごろ改善どころかますます悪化してきたため、とうとう対処することにした。応答が遅いのなら、地図タイルを大きくしてそのぶん要求数を減らせばよい。しかし値を変えるだけでは済まず、複雑な改修を要するコードもあったため、これまで躊躇していた。
クラスター
地図アプリにおいては、表示・操作に難があるほど多量の情報群を階層的にまとめて代表表示する働きを指す。具体機能としては別物だがわかりやすい例は、たとえば地図上の行政区のラベルがズームレベルに応じて市区町村名・都道府県名・国名に切り替わるなど。
座標変換について
将来、ウェブ地図ライブラリーが任意の座標系に対応し、こんな面倒な変換は不要になるかもしれない。カスタム座標系を望む声はやはり多いとみえ、MapLibre GL JSのロードマップにも掲げられている。実現すれば、非地理空間情報も扱いやすくなりそう。
WebGL
GPUを駆動する、ウェブ標準のグラフィックスライブラリー。
WebGLでマップチップのような2D表現をするのは、いきなり数学を強いられずに済み入門にも適するはず。ちなみに、後継と目される「WebGPU」は最近(2025年末ごろ)ようやく全主要ブラウザーが対応した段階。充分に普及し実装が成熟するにはもうしばらくかかるだろう。
頂点/フラグメントシェーダー
「各ポリゴン頂点の画面上座標や属性情報を決定し」、「そのポリゴンをピクセルとして描画する」GPUの処理工程における、前者のコードを頂点シェーダー、後者のコードをフラグメントシェーダーと呼ぶ。
ポイントスプライトについて
実は、WebGL仕様はポイントスプライトの表示可能サイズを規定していない。保証しているのは1×1ピクセルの文字どおり「ポイント」のみ。表示可能なスプライトの大きさはGPUやドライバー次第なのだ。ただし、今日ほとんどの環境で少なくとも256ピクセル程度まで表示できると考えてよい。Zipanguelikeの実装はこの事実に依拠し、最大128ピクセルのポイントスプライトを用いている。
ごく古い環境では64ピクセルが上限だったりして、その場合はあるズームレベルでマップチップが破綻してしまうが、仕方ない。そうした環境ではよその多くのWebGL演出も破綻しがちで、ユーザーは奇妙な表示におそらく慣れているだろう。
iOS版Safariについて
何かと厄介。ブラウザーの多様性は歓迎するものの、ウェブ標準仕様の解釈が“多様”だったり、仕様を取り入れるのが他より数年単位で遅かったり、機器が高価なくせに石器時代のごとき制約があったり、挙動がコロコロ変わったり、バグが多いのに更新が遅かったり、更新されたら余計バグが増えたり、にもかかわらず他のブラウザーに対して排他的だったりするのは端的に困る。WebKit(基礎となるオープンソースのブラウザーエンジン)は問題ないのにSafariだけおかしいことも。
プロシージャル(手続き型)生成
データをアルゴリズムで生成する手法。手作業を省いたり、無数のバリエーションを生み出したりするために用いられる。有用な反面、数値的にはユニークでもユーザーにとっては単調でしかない“無数のバリエーション”の乱造に陥る危険もある(「プロシージャル・オートミール」問題)。
「256×256マスに分割」について
ファミコン版『ドラクエ』シリーズのフィールドマップは256×256マスらしい。つまり4次相当メッシュの格子一つひとつがドラクエの世界並みに広がるわけだ。結果Zipanguelikeの総マス数は膨大になり、国土だけで1千億マス、海も数えれば現実装のカバー範囲は3兆マスを超える。
どうせプロシージャルなので、『パワーズ・オブ・テン』(あるいは『たまごっちパラダイス』?)よろしくさらに粒度を上げることも理論上は可能だが、ズームレベルを上げすぎるとMapLibreの動作が怪しくなってくるようだ。
地形のプロシージャル生成とローグライクについて
プロシージャルな地形生成やディテールの補完は、大規模なマップを持つ現代のコンピューターゲームでは当たり前に行われている。その先駆けとして知られるのが、1980年ごろアメリカで開発され流行したダンジョン探索RPG『ローグ(Rogue)』。ダンジョンが毎度プロシージャル生成され、繰り返し新鮮に遊ぶことができる。「ローグライク」とはこのコンセプトを受け継ぐゲームの総称で、オリジナルのレトロな美学を受け継ぐものも多い。
Zipanguelikeの命名はこれに由来し(プロシージャル生成部分は架空の地図となるため「日本もどき」との意味合いもある)、表現もあえてJRPGではなくローグライクに寄せている。そのほうがより素朴で制作・開発しやすく、プロシージャル生成の単調さとも調和する(描写が詳細だとかえって単調さが際立ってしまう)。
「CPUには重荷」について
ターゲットを高性能機器に絞ればCPUにさせてもいいかもしれない。地図タイルの生成処理は常時走るわけではないし、地図タイルサーバーからロードするより目立って遅くならなければ問題ない。マップチップ個別にではなく包括的に処理するCPU向けプロシージャルアルゴリズムも考え得るし、WebAssemblyで効率化する余地もある。
「即時応答の動的生成」について
「タイル要求時にではなく、事前に(アプリ起動時に)すべてをプロシージャル生成しておけばいいのでは?」と思われるかもしれないが、範囲が広すぎて非現実的。メモリーと時間を膨大に費やし、サイトのリソース割当とユーザーの忍耐力を超えてしまう。「すべて」ではなく、大まかなヒント情報や家々のプレハブくらいなら作ってみてもいいかもしれない。
「標準乱数には再現性がありません」について
Math.randomにシード値を与える関数がない。ただし厳密にいえば強いて与えられないこともなく、セキュリティーや課金・決済が絡む場合は要注意。
「違う地形が表示されてしまう」について
うちのデスクトップPC・タブレットPC・スマートフォンそれぞれで違う地形が表示されて往生した実体験による。ここでは否定的だが、ローグのようにあえて千変万化させたい(同一シード値による再現性すらいらない)のなら問題ない。
ノイズテクスチャーについて
制御しやすい一方、テクスチャーの参照は比較的重く、規則性も現れやすいことに注意。
自作マップチップ表示機能について
わが国でドット絵地図に求められているのはむしろJRPG風、言ってしまえば『ドラクエ』風だろう。しかし権利上そのまま再現するわけにはいかず、また仮にそうしてみたところで、ぱっと見わかりやすい反面いまさら感があり興味に欠けるのではないか(ゲームに非はなくあくまで地図ネタとして)。よってこちらとしては採用しないが、欲しければユーザーご自身でどうぞ、と委ねるために用意した機能でもあった。
自作マップチップのテンプレートを適用した様子
いろいろ言いつつ、用意した自作マップチップのテンプレートはJRPG風
サーバーレス
サーバーに関する要素や労力を何かしら省けることを誇張したマーケティング用語で、実態はさまざま。ブラウザーに応答する存在が皆無なわけでは無論ない。ここでは、「膨大なデータを提供する地図タイルサーバーを用意しなくてよい」との意味で使っている。
「安価に公開」について
Zipanguelikeのファイル一式は現時点で計4メガバイト弱(転送時はさらに圧縮される)。すべて静的ファイルでデータベース不要、初回ロード後の通信量はわずかでオフライン動作も可。したがって各種クラウドサーバーや“サーバーレス”CDN類の無料利用枠に余裕で収まり、固有のサーバーサイド実行環境に依存することもない。ホスティングサーバーを借りるとしてもごく安いプランで充分、小型マイコンでの自家ホスティングも無理なく可能。独自ドメインで公開するならその費用がかかる程度。

補遺

地図タイルの描画における座標系

地図タイルは16×16個のマップチップで構成されます。これをWebGLで描くとき、シェーダーはメッシュをどのように参照すればいいのでしょうか。

メッシュは「XYZ形式タイルにおけるズームレベル16の粒度」の配列であり、それを入力テクスチャーとして渡しています。ならば、このテクスチャーのどこかからどこかまでの16×16テクセルを参照すればいいはずです。

その「どこか」とはどこか。いま描こうとしている地図タイルの、ズームレベル16におけるXY座標です。元のXYZ座標をそれに変換(Zが1増えるごとにXY軸の目盛りは倍になる計算)し、ユニフォームすなわち入力変数としてシェーダーに渡してやればいいのです。シェーダーは、渡された座標をテクスチャー上の起点とし、現在のズームレベルに応じた増分で(つまり飛び飛びに)テクセルを参照していきます。

まとめると要するに、ズームレベルによって異なるXYZ形式地図タイルのXY座標系を、メッシュのXY座標系、言い換えれば「グローバル(と定めた)座標系」に変換・統一してやるわけです。

プロシージャル生成における地図タイルの連続性

些細な事柄を前節でわざわざ述べたのは、プロシージャル生成においてもその「グローバル座標系」が重要だからです。

プロシージャル生成される地形は、XYZタイル座標系のメッシュに基づいてはいるものの、地図タイルの境界に沿ってなどいません。本文で説明したとおり四辺がフラクタルにずれ、チップが隣のタイルへはみ出したり、隣のタイルからはみ出てきたりします。

いっぽう地図タイルは各々個別に描画され、隣接タイルについて関知しません。にもかかわらず、境界をまたぐフラクタル地形がちゃんとつながるのはなぜでしょう。さらに、ズームレベル間でも矛盾なく同じ地形を保つのはなぜでしょう。

答えは簡単、フラクタル関数の引数に「地図タイルないしマップチップのグローバルXY座標」を使っているためです。入力値から出力値が自ずと決まる関数の性質により、地図タイル間の連続性が自然に担保されます。たとえば、入力値100までの関数グラフと100からの関数グラフとは、別々に描いてもぴったりつながる道理でしょう。乱数を求める際も、グローバルXY座標をシード値として与えます。

開発環境

Zipanguelikeの開発環境について。環境というほど大層なものではありません。基本的に、秀丸エディタと静的サイトジェネレーター(SSG)Hugoだけで作っています。OSはLinuxで、秀丸エディタはWindows互換実行環境上で無理やり動かしています。

単一ページのウェブアプリならSSGは不要そうですが、あればミニファイやハッシュ生成など公開向けのちょっとした処理を自動化でき、いちいちスクリプトなど書かずに済んでラクです。もちろん、こうした記事ページも簡単に追加できます(とはいえサービスワーカーを用いるウェブアプリの場合、ドキュメントなどはサブドメインに分けたほうが何かと都合がいい、とのちに気づいた)。

既製のデザインテンプレートは使わず、HTML・CSSはすべて手書き。JavaScriptは、MapLibreを別にすればやはりすべて手書きです。シェーダーも、やることが特殊すぎて自前で書くしかありません。

画像の編集には、大昔に買ったCS6版のフォトショップをいまだに使っています。タイルセットのようなドット絵の編集には定番のAsepriteが適していそうと思いつつ、操作を覚えるのが億劫で、インストールしたのに全然使っていません。唯一、バナーのアニメーションGIFを作るのに使ってみたくらい。

地理・統計データの加工にはPHPを使いました。入力形式はいずれもXMLかCSVなので、それらを簡単に扱える処理系なら何でもかまいません。PHPは単に慣れているのと、環境問わず無難に動くためよく利用します。

WebGPU対応

ZipanguelikeをWebGPUに対応させました。ただし、WebGPU非対応のブラウザーではWebGLにフォールバックします。

2026年9月現在、WebGPUはすべての主要ブラウザーが一応サポートしているものの、実装状況がまちまちで使うのは時期尚早だと思います。にもかかわらず対応したのは、iOS版Safariの不具合(?)のせい。先日リリースされたiOS27.0において、サービスワーカー内でオフスクリーンキャンバスのWebGLコンテキストをどうしたわけか取得できなくなってしまったのです。が、なぜかWebGPUコンテキストは取得できることに気づきました。ならばそちらを使うほかありません。そのWebGLからWebGPUへの移植について、詳細を別記事に書きました。