SVX日記

2004|04|05|06|07|08|09|10|11|12|
2005|01|02|03|04|05|06|07|08|09|10|11|12|
2006|01|02|03|04|05|06|07|08|09|10|11|12|
2007|01|02|03|04|05|06|07|08|09|10|11|12|
2008|01|02|03|04|05|06|07|08|09|10|11|12|
2009|01|02|03|04|05|06|07|08|09|10|11|12|
2010|01|02|03|04|05|06|07|08|09|10|11|12|
2011|01|02|03|04|05|06|07|08|09|10|11|12|
2012|01|02|03|04|05|06|07|08|09|10|11|12|
2013|01|02|03|04|05|06|07|08|09|10|11|12|
2014|01|02|03|04|05|06|07|08|09|10|11|12|
2015|01|02|03|04|05|06|07|08|09|10|11|12|
2016|01|02|03|04|05|06|07|08|09|10|11|12|
2017|01|02|03|04|05|06|07|08|09|10|11|12|
2018|01|02|03|04|05|06|07|08|09|10|11|12|
2019|01|02|03|04|05|06|07|08|09|10|11|12|
2020|01|02|03|04|05|06|07|08|09|10|11|12|
2021|01|02|03|04|05|06|07|08|09|10|11|12|
2022|01|02|03|04|05|06|07|08|09|10|11|12|
2023|01|02|03|04|05|06|07|08|09|10|11|12|
2024|01|02|03|04|05|06|07|08|09|10|11|

2024-03-11(Mon) そんなメルカトル、補正してやるッ!!

  ついに走り出したところで、次はなにを実装すべぇかなぁ、と考えなしに考えていたら、我ながら意外なところに向かってしまった。緯度補正だ。Google Map(ほかオンライン)の地図データはメルカトル図法なので、北に行くほど「より大きく」表示されるのだ。

  当初、そんなもん大した違いではないから無視しよう、と思っていたのだが、実は無視していいレベルの違いではなかった。だいぶ違う。一応は、実際に遊んだときに実際のF1のラップに近いタイムが出るようにしたいと思っているのだが、そうしたければとても無視できないレベルだ。

  とりあえず「鈴鹿」「シルバーストン」をテストの対象に開発を進めていたのだが、こうなれば赤道に至近なサーキットもテストの対象に加えるべきだ。最近はF1観てないんだよなぁ……なので、ググる。すると「マリーナベイ・ストリート・サーキット」がそれらしい。シンガポールGPが開催されている市街地サーキットで、北緯1度17分にある。ほぼ、赤道直下といっていい。

  画像の説明 画像の説明 画像の説明

  で、なにげなく座標データだけ入力してやると……「ちっさ」。左から「シルバーストン」「鈴鹿」「マリーナベイ」である。「シルバーストン」では気づかなかったが、こりゃ補正不可避である。幸い、開発したばかりの「BG版の回転拡大縮小機能」は、特段の負荷なしに自在に拡大縮小が可能だ。で、ここからは数学の時間である。まずは、一番直感的に書けるRubyで補正値を求めるプログラムを書いてみた。

include Math
 
# equ_px = (2 **  0) * 256                      # 赤道のピクセル数(ズームレベル0)
equ_px = (2 ** 20) * 256                        # 赤道のピクセル数(ズームレベル20)
equ_m = 40075 * 1000.0                          # 赤道の周長(m)
 
p equ_1px = equ_m / equ_px                      # 赤道下の1ピクセルの長さ(m)
#=> 0.1492910087108612
 
car_px = 24                                     # 車幅のピクセル数
car_m = 2.0                                     # 車幅(m)
 
p car_1px = car_m / car_px                      # 車の1ピクセルの長さ(m)
#=> 0.08333333333333333
 
p equ_times = car_1px / equ_1px                 # 赤道下の補正値
 
p 256 * equ_times                               # 回転拡大縮小機能への補正値
#=> 142.8976434518611

  まずは、赤道直下を対象にした補正値だ。使用する定数は「赤道の周長(40075km)」と「F1の車幅(2.0m)」だ。どちらもWikipediaで調べた。ゲームとしての基本的な仕様はスーパーフォーミュラをパク……オマージュるつもりなので、車のサイズは24x43ピクセルだ。2.0mを24ピクセルで表現したい、ということになる。回転拡大縮小機能に与える補正値は、サンプリングベクトルで256が標準値であり、それより小さい値を与えると拡大される。計算の結果、赤道直下の場合は143を与えればいいと出た。

  次は、緯度補正だ。緯度が高くなるにつれ地球の周長は減少していくが、それはコサイン一発で求められる。

p   [lat = 50, 'イギリス' ]
# p [lat = 35, '日本' ]
# p [lat =  0, '赤道' ]
 
p cos = Math.cos(lat * PI / 180)
p lat_times = car_1px / equ_1px / cos           # 任意の緯度下の補正値
 
p 256 * lat_times                               # 回転拡大縮小機能への補正値
#=> 222.30926872026404                          # [lat = 50, 'イギリス' ]
#=> 174.44581191992688                          # [lat = 35, '日本' ]
#=> 142.8976434518611                           # [lat =  0, '赤道' ]

  日本の場合は174、イギリスの場合は222を与えればいいと出た。概ね45度が7割なんだから合っていそうだ。

  次は、CoffeeScriptへの組み込み。ほぼ、上記のRubyのコードがそのまま動いたが、ひとつ考慮することがある。車を南北に移動した場合、補正値も変化させるべきか? ということだ。さすがに1フレームの移動毎に計算するのは過剰で、タイルをまたがったタイミング毎で十分だろうから処理は軽い。ゲーム内で使用する座標情報はWposというクラスで管理しているから、それに関数を追加するか……と実装しかけて気づいた。Wposクラスは経緯度で座標を与えられる仕様ではあるが、直後にメルカトル座標に変換して保持し、緯度情報は破棄してしまうのであった。ゲーム中の車の座標管理ならメルカトル座標のが扱いやすく、緯度を継続的に保持する必要性はないからだ。

  つうわけで、今回の目的は主にサーキットを走るのがメインであって、大陸縦断をするわけではないので、サーキットを選択した時点で補正値を計算し、それ以後の補正値の更新はなしとした。結果、コードは以下のようになった。

equ_times = car_1px / equ_1px                   # 赤道下の補正値
@lat_index = equ_times * 65536                  # 緯度の補正指数
@t = Math.round(@lat_index / Vec.v2vxy(128 - Math.round((@car.lat0 * 64) / 90.0), 1)[0])

  コサイン値は既存のVecクラスのテーブルの参照で済ませる。テーブルは90度を64分割、1.0を256として保持しているので上記のようになる。そして、表示させたスターティンググリッドが以下だ。

  画像の説明 画像の説明 画像の説明 画像の説明

  うぉーッ!! スターティンググリッドの位置が見事に揃いましたゼ、ダンナッ!! プログラミングって、こういう感じにわかりやすく美しい結果を得られた時が、たまらなく楽しい瞬間なんだよなぁ。これも今回、調べて初めて知ったことなのだが、スターティンググリッドの間隔は8mと決められているらしい。

  それではと、画面上のスターティンググリッドの間隔を測ってみると、ポールポジションと3番手のグリッドの間隔は192ピクセル。今回は2.0mを24ピクセルで表現しているのだから……16mッ!! パーフェクトだウォルター。

  ……と、ふと気になって、パク……オマージュり元のスーパーフォーミュラはどうなのかとおもったら……あれ? 狭い……つうか、ちょうど半分の間隔になってる!? なんだこれッ!? 偶然ッ!?