球体世界を航海するゲームを作っていたら「でたらめ海図」が一番面白くなった話
個人制作で大航海時代風ゲームを作っています。
リンク:
大航海時代の地図は最初から完成しているものとして扱われることが多いです。
しかし実際の航海者たちは、世界の形を知らないまま海へ出ていました。
そこで私は、
「球体世界を航海しながら自分で海図を作るゲーム」
を作り始めました。
ところが開発を進めるうちに、
正確な海図よりも
クルーの能力不足で歪んだ海図や、情報が欠けた『でたらめ海図』の方が圧倒的に面白い
ことに気づきました。
今回はその実験について紹介します。
2枚目のGIFでは飛空艇が北極・南極をそのまま通過しています。球面上では連続した移動として処理されており、ジンバルロックも発生しません。メルカトル図法上では左右へ自然につながる挙動になります。
極付近で正距方位図法が回るので困る
ではなく
球面上の局所接平面が動いているので当然回る
という発想になっている時点で、かなり地図屋さん寄りの考え方ですね
最初は
- 球面上の移動
- 四元数による姿勢管理
- ジンバルロック回避
- 正距方位図法表示
といった数学・3Dエンジン寄りの実験でした。
ところが実装を進めていくうちに、思わぬところで面白いゲーム性が生まれました。
それが
「測量ミスで間違った海図ができるシステム」
です。
球体世界をそのまま航海する
一般的な2D海洋ゲームではメルカトル図法の座標上を移動します。
今回の実装では地球をそのまま球体として扱い、
ShipPos
に単位球上の位置ベクトルを保持しています。
前進時は
NewPos = NewPos.RotAxis(
EarthUp.Cross(Forward).NormalVec(),
forwardDelta
).NormalVec();
のように球面上を回転させることで移動しています。
つまり、
「緯度経度を書き換える」
のではなく、
「球面上で船そのものを回転させる」
という発想です。
極地でも破綻せず、世界一周も自然に表現できます。
地図は正距方位図法
局所航海図には正距方位図法を採用しています。
プレイヤーを中心に展開するため、
- 北極付近でも歪みにくい
- 球面感覚が分かりやすい
- 周囲との距離感を直感的に把握できる
という特徴があります。
昔から
大航海時代のゲームはなぜメルカトル図法ばかりなのだろう?
と思っていたので挑戦してみました。
ランダム生成の球体惑星
地形生成は SimplexNoise を利用しています。
let elevation =
simplex.noise3D(cx/3000, cy/3000, cz/3000);
elevation +=
simplex.noise3D(cx/1000, cy/1000, cz/1000)*0.3;
球面上に直接ノイズを貼り付けているため、
継ぎ目のない惑星が生成されます。
経度180度問題を気にしなくて済むのが球面生成の大きな利点です。
港を実装
ある程度海にパターンができてきたので港も実装しました。
港は海岸線付近へランダム生成されます。
if (terrain.isLand && terrain.elevation <= 0.25)
さらに近隣港との距離制限を設け、
if (distKm < 2000)
で密集しないようにしています。
船も実装
船種データはマスター形式です。
const SHIP_TYPES_DATA = [
{ name:"スループ", ... },
{ name:"フリゲート", ... },
{ name:"ガレオン", ... }
];
港では船の乗り換えができます。
現状ではデバッグ用途ですが、
将来的には
- 航海速度
- 積載量
- 船員数
- 大砲数
などへ反映する予定です。
測量士モードを実装してみた
ここからが本題です。
地図埋め要素を作っている最中に考えました。
現実の大航海時代の測量士って、
本当に正しい地図を描けていたのか?
神測量士モード
まず普通のモードです。
exploredWorld[index] = terrainData;
観測した場所を正しい位置に記録します。
一般的な探索ゲームと同じです。
へぼ測量士モード
次に測量誤差を入れてみました。
driftAngle += RandomDrift;
航海するたび少しずつ方位誤差が蓄積します。
観測時には
const heboScanAngle =
angle + driftAngle;
を使用します。
すると現在地の認識が少しずつ狂い始めます。
起きた現象
これが予想以上に面白かった。
例えば実際にはこうです。
本当の位置
↓
□□□□■□□
しかし測量士は
認識している位置
↓
□□■□□□□
のように思い込んでいます。
測量結果は正しいのですが、
記録場所が間違っています。
つまり
地形は正しく観測しているが、海図が間違う。
プレイヤー自身が騙される
普通のゲームでは
- プレイヤー
- システム
の両方が正しい地図を見ています。
しかし今回のシステムでは
ShipPos
と
estimatedShipPos
を別々に持っています。
その結果、
プレイヤーが見ている世界地図そのものが嘘になります。
港に入ると測量し直す
さらに面白かったので、
港で位置補正できるようにしました。
estimatedShipPos = ShipPos;
driftAngle = 0.0;
入港すると現在位置が確定します。
すると、
今まで信じていた海図と現実の位置関係が突然変わります。
思わぬゲーム性
当初は
- 球面移動の検証
- 正距方位図法の実験
- 四元数の実験
のつもりでした。
しかし気付けば、
未知の海を探検するゲーム
ではなく、
正しい世界地図を作るゲーム
になっていました。
さらに言えば
プレイヤー自身の認識を騙すゲーム
に近いかもしれません。
今後やりたいこと
今考えている要素です。
- 天文学者雇用
- 航海士雇用
- 測量士雇用
- 交易品
- 港ごとの相場
- 海賊
- 嵐
- 乗員管理
特に
「優秀な測量士ほど正しい海図を作れる」
というシステムは相性が良さそうです。
おわりに
球面移動や四元数をいじっている頃は完全に数学実験でした。
ところが実際に港や船を実装し始めると、
一番楽しくなったのは数学部分ではなく
「でたらめな海図」
でした。
ゲーム開発をしていると、
作りたかったものとは別のところに面白さが発生することがあるんだなあと実感しています。
むしろ今は、
「未知の大陸を発見する」
よりも
「その海図、本当に合ってますか?」
の方が面白そうでワクワクしています。
リンク:
作者
GitHub: https://github.com/NAS6mixfoolv
X(旧Twitter): https://x.com/NAS6_oxo
作者HP: https://nas6.net
気に入っていただけたら GitHub に ⭐ をいただけると嬉しいです!

