Simutrans本体2018年総まとめ
どうも、ひめしです。
先日12/8に開催されましたシムトラ学会in京都でお話した内容のスライドを公開します。(イベントのハッシュタグは #シムトラ学会4 です。)2018年にあったSimutrans本体の機能追加を総ざらいする内容です。(extendedは除いています。)
後日ありすさんから発表の動画が上げられるとのことですので、そちらもよろしくおねがいします。
【12/15追記】
学会の動画が上げられましたので貼っておきます。
なぜ振り分け信号の手前で列車は減速するのか
どうも、ひめしです。コードを書く気にも本を書く気にもなれないので記事を書きます(
Simutrans standardには振り分け信号というものが用意されています。あいている適当なホームを見つけて入線する信号ですが、デフォで設定した経路以外に入線するときはきまって信号手前で減速します。これはなぜなのでしょうか?
これを理解するにはsync()とsync_step()の説明が必要です。
simutransは車両、建物、道路などといったすべてのオブジェクトに対して定義されたsync()とsync_step()という二種類の関数を延々と呼び続けることでシミュレーションを実現します。「すべての」オブジェクトかどうかはあやしいですが。鉄道車両が一番わかり易いので以降は鉄道車両を題材に説明します。
鉄道車両の場合、毎フレーム画面を描画するたびに車両の位置や速度が変化していきます。したがって毎フレームごとにこれらを再計算しなければなりません。画面を描画するたびに(正確には画面を描画する前に呼ばれる)行うシミュレーション処理は基本的にsync_step()にかかれています。convoi_tに定義されたsync_stepの冒頭は以下のような感じです。編成のstateごとに定められた処理を行っています。(コードの中身を読む必要はありません。)
sync_result convoi_t::sync_step(uint32 delta_t) { // still have to wait before next action? wait_lock -= delta_t; if(wait_lock > 0) { return SYNC_OK; } wait_lock = 0; switch(state) { case INITIAL: // in depot, should not be in sync list, remove return SYNC_REMOVE; case EDIT_SCHEDULE: case ROUTING_1: case DUMMY4: case DUMMY5: case NO_ROUTE:
ところで位置の更新や速度の計算は比較的少ない計算量で終えることができますが、ルート探索などは処理が重いので1描画フレームの時間内に処理を完結させることが困難なことがあります。マップ内に10000編成いるならば、描画する間に10000編成分のsync_step()を回し終わらねばならないわけです。
そこで、「処理量は大きいけど急を要さない(1フレームの間に処理を完了させる必要がない)」処理を回す関数としてstep()が用意されています。実際、convoi_t::step()を見ると、主に
- ルート探索
- 停車状態での発車判定・発車処理
- 貨客の積み下ろし
が行われていることがわかります。これらの処理はいずれも車両が停止している間に行われています。車両が停止しているならば画面が動かない限り再描画の必要はなく、位置や速度といった描画に直接関わるパラメータが変化することもないのでテキトーに処理をはじめてテキトーなタイミングで終わればよいのです。
さて、Simutransは少なくともstandardでは描画まわりはマルチスレッド化されているものの、シミュレーション部は未だ完全なシングルスレッドです。シングルスレッドのプログラムですから処理は定められた順番に逐次行われていくのですが、sync()とsync_step()はどのように呼び分けられるのでしょうか?
実際のCPU時間の使われ方を簡単な図にすると、下のようなものになります。基本的にはsync_step()・step()両方とも、クロックを持つ外部オブジェクトから定期的に呼ばれています。もう少し具体的に言うと、呼び出し間隔が決められていて、前回の呼び出し時間から定められた時間経過すると、関数が呼び出されるチャンスが与えられるという仕組みです。
例えば描画レートが30fpsの場合、sync_step()の最後の呼び出しから1/30秒たつと(実際にはちょっと違いますが)sync_step()を呼び出すことが可能になり、sync_step()が呼ばれます。その結果図のようにsync_step()が概ね定間隔で呼ばれます。
さて、CPUがあいていればsync_step()が適切なタイミングで呼ばれますが、もし大きな実行時間を要するstep()が実行中だとsync_step()を呼び出すタイミングが遅くなってしまいます。これでは画面描画が遅くなりカクつく原因になります。そこで、step()の中では時折interrupt_check()を呼び出して、sync_step()を呼び出すことになっています。ソースコードを覗いたことがある人なら INT_CHECK という文字列に見覚えがあるのではないでしょうか?simintr.hにこのマクロは定義されており、INT_CHECKを踏むと、interrupt_check()を呼び、sync_step()を呼ぶ必要があるタイミングであるかチェックするのです。下のコードはdataobj/route.ccの一部で、INT_CHECKがコードに挿入されている例です。引数の文字列はデバッグ用途のものです。
// memory in static list ... if( nodes == NULL ) { MAX_STEP = welt->get_settings().get_max_route_steps(); nodes = new ANode[MAX_STEP]; } INT_CHECK("route 347"); // we clear it here probably twice: does not hurt ... route.clear();
このようにしてsimutransはsync_step()をできるだけ等間隔に呼びつつ、その合間に処理量の大きい処理を行っています。さて、振り分け信号の減速問題に話を戻すことにしましょう。
列車が動いているときの処理は基本的にsync_step()で行っています。列車が進めば次の閉塞に進入可能か判断する必要があります。rail_vehicle_t::is_signal_clear()がその関数ですが、この関数はsync_step()内で呼ばれます。ですので、振り分け信号を目の前にしたときの処理もsync_step()が行うことになります。
ところで、振り分け信号はまず予め指定した座標に入線できるかチェックします。もしこの時点で進入許可が出れば、列車はそのまま進行すればいいのでsync_step()内で処理は完結します。
ところがここで入線できないということになると、空きホームを探さねばなりません。空きホームを探すのは探索処理にあたり、sync_step()ではなくstep()内で行うことになっています。step()は処理の完了時間が予測できないので、列車が走行中に行う処理を記述することはできません。そこで空きホームを探す探索処理を行うために、列車を一度停止させてstep()を回す必要があるのです。
というわけで厳密には列車は一度停止しているのですが、その停止は一瞬なので見かけ上はただ減速してるように見えます。これが振り分け信号の手前で列車が減速する理由です。
なお先ほどのお話には大きく2つのツッコミどころがあります。
- 振り分け信号の探索処理は探索空間がさほど大きくないので実はsync_step()内で行っても大丈夫なのではないか?なぜstep()内で行う必要があるのか
- 列車が走行中に行う処理をstep()にやらせることは本当に不可能なのか?
このあたりの話はマルチスレッド化にも深く関わるところでまた大きなトピックになりますので、別の機会に解説することにしましょう。
とりあえず今回はここまでにします。お読みいただきありがとうございました。
続・アドオンの減色について
前回はアドオンの減色のしくみについて解説したので、今回はなぜ減色がされているのか、今後減色が解消される見込みはあるのかなどについて考えたいと思います。
himeshi.hateblo.jp
アドオン減色について語るには、まずSimutransの描画処理まわりをおさらいする必要があります。
Simutransに限らず大半のGUIプログラムは、描画処理を何らかのライブラリに任せる形で行っています。言い換えると、描画ライブラリと呼ばれるプログラムに対して描画命令をすることで画面が描画されるということです。Simutransは描画ライブラリとしてallegro、GDI、OpenGL、SDL、SDL2をサポートしています。今回は描画ライブラリをSDL2として話を進めることにします。
なお描画ライブラリの命令は描画ライブラリごとに異なるので、simsys.hが描画命令を共通化する役割を持っています。SDL2の場合の実装ファイルはsimsys_s2.ccです。
描画手順は各ライブラリごとに若干異なりますが、概ね次の手順で行われます。まずSimutransは描画するピクセルデータを描画バッファと呼ばれる変数に格納します。描画バッファにピクセルデータを格納し終えたらライブラリの画面更新命令を呼びます。これで、描画バッファに格納されたピクセルデータが画面に反映されるのです。
Simutransの描画処理は、地面を描き、線路を描き、車両を描き・・・と続いていきます。地面画像の上に線路画像や車両画像が塗り重ねられていき、最終的にすべて塗り重ねた結果を描画ライブラリに渡しています。よってピクセルを塗り重ねていく処理はSimutrans自身が行うことになります。塗り重ねの処理はdisplay/simgraph16.ccにかかれています。なおsimgraph0.ccはGUIを起動しないサーバーモード用の処理であり、描画処理を行いません。
simgraph16.ccの386行目で、描画バッファ変数は次のように宣言されています。
/* * Output framebuffer */ static PIXVAL* textur = NULL;
PIXVAL型の実体はuint16です。この時点で、画像の塗り重ね処理自体が16bit整数で行われていることがわかります。減色されたアドオン画像を256階調RGBに復元しているわけではないのです。ちなみに画像のアルファ値はライブラリに渡されるのではなく、simutrans自身が重ね塗りをするときに吸収しています。
さて、描画処理自体が16bit(内訳はRGBそれぞれ5bitずつで本当は15bit)で行われていることがわかりましたが、32bitで扱うのとくらべてどのような違いがあるのでしょうか?大きな違いは主に2つです。
- 描画処理のパフォーマンスに差が出る
- アドオンpakファイルサイズが2倍になる
32bit化するとアドオンサイズが2倍になるのは良いと思います。1ピクセルにつき従来は16bitで表現されていたものが32bitで表現されますからね。
描画処理のパフォーマンスに差が出るというのは、まずピクセル計算に必要なメモリが倍になります。それに伴ってメモリとCPUのデータやりとりも2倍になります。ひめしはあまりグラフィックに詳しくないのでこれがどのレベルまでパフォーマンスに影響を与えるかはよくわかりません。
ともかく、色を32bitで扱うようにするとストレージや処理系の負担になることは事実です。が、近年のコンピュターのパフォーマンスを考えればそろそろ32bit化をしても良い気はしてきます。実は32bit化の足かせになるのはここではありません。Simutransの描画系が16bitから32bitに移行するに当たって困難なのは次の二点です。
- 実装量が膨大であること
- アドオンの互換性を担保しなければならないこと
画像描画まわりのコードは膨大です。新しく32bitでの描画をサポートしようとすれば、最低限display/simgraph.hで宣言されている関数をすべて実装しなければなりません。simgraph.hは宣言ファイルですが、このファイルだけで381行あります。16bit色描画を実装したsimgraph16.ccは5336行あります。
実装するのはsimgraphまわりだけではありません。makeobjでpng画像をコンパイルする過程もそうですし、なによりGDIやSDLといった各ライブラリとのインターフェースも32bit色表現に対応させる必要があります。すべての描画ライブラリできちんと動作することを担保せねばなりません。
ちなみに現行Simutransがアルファ対応をするときに32bit化せず16bit空間でゴリ押したのは実装量低減とパフォーマンスの問題とされています。(ソース:
https://forum.simutrans.com/index.php/topic,15314.0.html
)
アドオンの互換性も大きな課題です。Simutransにはプレイヤーカラーや発光色といった特殊色があります。16bit色環境ではこれらは特殊色として専用の値を割り当てていましたが、32bit色環境でも似た処理を採用するのが望ましいでしょう。するとARGB各成分それぞれフルに8bitを割り当てるわけには行きません。おそらくALPHA成分は5bitにするといった策が現実的になると考えられます。
32bit色環境は間違いなくパフォーマンス面で悪影響をもたらすので、オプションとして提供されるのが望ましいでしょう。すると、アドオンは16bit環境と32bit環境の双方をサポートする必要が出てきます。16bit色環境実行時のパフォーマンスが落ちては元も子もないので、32bit色でコンパイルされたアドオンを16bit色環境でスムーズに表示できるよう工夫が必要です。16bit画像をpakに含めてしまうのは一つの策ですが、明らかにファイルサイズが無駄に大きくなります。32bit色表現で、16bit色環境実行時にスムーズに読み出せるbit配置に工夫するのが良い作戦かと思います。
だらだらと書きましたが、要点はこんな感じです。
- 減色ナシ化はパフォーマンスが悪化する。それを受け入れれば可能。(ただしアルファチャンネルは若干のビット落ちがあるかも)
- 本体を減色ナシに対応させるのであれば、減色版本体も同時に提供する必要。
- 実装はかなり大変。
- アドオンの互換性をどうするか(減色環境での軽量動作をどう担保するか)の検討が必要
アドオン製作者の皆さんはどうお考えでしょうか?
アドオン画像の減色について
アドオン画像の減色メカニズムについて、コードを読みながら整理したいと思います。
本来この話は現在執筆中の『Simutransの本体開発』に載せるつもりでしたが、本の構成上溢れそうなのでこちらで供養するといったものです。Simutransの仕様は数年もすれば様変わりしてしまいますが、いつ何時も原典であるコードの読み方といっしょに分析しておけば時代が下っても参考程度にはなるかなと考え書き残しておきます。
減色処理はpngとdatからアドオンをコンパイルするときに行われるのでdescriptor/writer/image_writer.ccを見れば良いことになります。コードはこちらからオンラインで見れます。
きちんと流れを追うのはしんどいのでわかりやすいところからつまみ食いしていきましょう。62行目からはじまるpixrgb_to_pixval関数を呼んでいきます。名前の通り8bitなrgb値をSimutrans内部で使われているpixval値に変換する関数です。
static uint16 pixrgb_to_pixval(uint32 rgb) { uint16 pix; // first: find about alpha assert( rgb < ALPHA_THRESHOLD ); int alpha = 30 - (rgb >> 24)/8; // transparency in 32 steps, but simutrans uses internally the reverse format if( rgb > 0x00FFFFFF ) { // alpha is now between 0 ... 30 // first see if this is a transparent special color (like player color) for (int i = 0; i < SPECIAL; i++) { if (image_t::rgbtab[i] == (uint32)(rgb & 0x00FFFFFFu)) { // player or light color pix = 0x8020 + i*31 + alpha; return endian(pix); } } // else store color as 3 red, 4, green, 3 red pix = ((rgb >> 14) & 0x0380) | ((rgb >> 9) & 0x0078) | ((rgb >> 5) & 0x07); pix = 0x8020 + 31*31 + pix*31 + alpha; return endian(pix); } // non-transparent pixel for (int i = 0; i < SPECIAL; i++) { if (image_t::rgbtab[i] == (uint32)rgb) { pix = 0x8000 + i; return endian(pix); } } const int r = (rgb >> 16); const int g = (rgb >> 8) & 0xFF; const int b = (rgb >> 0) & 0xFF; // RGB 555 pix = ((r & 0xF8) << 7) | ((g & 0xF8) << 2) | ((b & 0xF8) >> 3); return endian(pix); }
前提として、一般的にピクセルの色はアルファ値を含めて32bitで表現されます。頭8bitが透明度、次の8bitがR、その次の8bitがG、一番最後の8bitがBを表します。
rgb > 0x00FFFFFFで分岐しているので、ざっくりALPHA値が含まれてるか否かで処理が分岐していることがわかると思います。なおココでは数値はすべて16進数で表記されています(頭に0xがついていたら16進数)。まずはALPHA値が入っていない処理から見ていきましょう。以下はpixrgb_to_pixval関数の後半部分を抜粋したものです。
// non-transparent pixel for (int i = 0; i < SPECIAL; i++) { if (image_t::rgbtab[i] == (uint32)rgb) { pix = 0x8000 + i; return endian(pix); } } const int r = (rgb >> 16); const int g = (rgb >> 8) & 0xFF; const int b = (rgb >> 0) & 0xFF; // RGB 555 pix = ((r & 0xF8) << 7) | ((g & 0xF8) << 2) | ((b & 0xF8) >> 3); return endian(pix);
与えられた色に対してプレイヤーカラーや発光色といった特別色に該当していないかまず検査します。特別色の場合は特別色番号に0x8000を加算した値をpixvalとして返します。pixval値は16bitなので最上位bitが1の場合は特別色であることを表すことがわかります(0x8000は2進数で表すと1000000000000000)。なので一般色に使えるbitは15bitしかありません。(ちなみに後で分かりますが、最上位bitが1のときは「透過ナシ一般色ではない」というのが真の意味です。)
特別色でない場合は、色の32bit表現からr,g,bを取り出した上で、各成分8bit(256段階)から5bit(32段階)にダウンサイジングしています。ビット演算で表現されてるので少々わかりにくいですが、やってることは256階調の色の値を8で割ってあまりは切り捨てです。例えばR成分が87であれば87÷8→10となります。最終的に返されるpixval値は16bitになります。元が32bitなのでデータサイズだけで見れば半分に圧縮されたことになります。
つづいてアルファ値が入っている場合を見てみましょう。以下はpixrgb_to_pixval関数の前半部分を抜粋したものです。
int alpha = 30 - (rgb >> 24)/8; // transparency in 32 steps, but simutrans uses internally the reverse format if( rgb > 0x00FFFFFF ) { // alpha is now between 0 ... 30 // first see if this is a transparent special color (like player color) for (int i = 0; i < SPECIAL; i++) { if (image_t::rgbtab[i] == (uint32)(rgb & 0x00FFFFFFu)) { // player or light color pix = 0x8020 + i*31 + alpha; return endian(pix); } } // else store color as 3 red, 4, green, 3 red pix = ((rgb >> 14) & 0x0380) | ((rgb >> 9) & 0x0078) | ((rgb >> 5) & 0x07); pix = 0x8020 + 31*31 + pix*31 + alpha; return endian(pix); }
やってることは概ね同じで、特別色を検査してから8bitのrgb値を圧縮しています。pixval値で一般色に使えるbit数は15bitなので、ALPHA値がない場合はRGB各成分5bit(32段階)で表現してましたが、今回はALPHA値の表現ですでに5bit(32段階)使っているのでRGBを10bitで表現せねばなりません。そのため、RとBは3bit(8段階)、Gは4bit(4段階)に圧縮されています。計算ルールは同じで、例えば元のRGB値でRが87であれば、87÷(256÷8)→2と計算されます。pixval値は16bitであり、その中で透過ナシ色や特別色と衝突しない表現をする必要があるので、最終的な値の計算式はやや複雑になります。
グラデーションの表現が不自然になることですでに悪名高い減色処理ですが、png画像にALPHA値を含めるとさらに激しく減色されるので、必要ない限りはALPHA値を含めることは避けるべきです。とはいえ大サイズ3Dアドオンが普及するにつれて減色問題による弊害は深刻さを増しているので、次回は減色がなぜ行われているのか、減色問題を解決する手立てはあるのかについて分析する予定です。
コミックマーケット C95に応募しました
表題のとおりです。冬コミに応募しました。サークル名は日本シムトラ学会名義です。
私自身はsimutrans本体開発入門本を出します。他にも数名の方から私のブースで本やCDを出すというオファーをいただいているので、日本シムトラ学会として楽しいブースにできるかなと思います。
私が執筆中のシムトラ入門本のメインターゲットは「プログラミングのお勉強はかじったことあるけどそこからSimutransの本体開発に繋がらない」という方です。もちろんプログラミング未経験の方でもC とC++の一部を自習していただければ十分ついていける内容になっています。既にソフトウェアの開発経験をお持ちの方にとってもシムトラ開発特有の知識をたっぷり解説していますのでスムーズな導入になるでしょう。シムトラ本体開発はOSSの改造ということになるのでそれ相応の知識と技能が必要になってきます。本ではそこを中心にカバーして、本体開発を楽しむ人が一人でも増えれば嬉しいなと考えながら書いています。
院試勉強からの現実逃避のおかげでページ数だけ見れば8割ほど書き上がりましたorz ですが、まだ最初に書きなぐった段階の原稿なのでこれから修正を重ねていくことを考えると楽観的に見てもまだ4割ぐらいの進捗でしょう。思いのほかページ数が増えそう(といっても技術本として考えればそんなに多くはない)なので印刷原価が上がるのが心配ですが、頒布価格は3ケタ円には抑えようと考えています。
初回頒布は冬コミ、ではなく、12月アタマに開催される予定の関西シムトラ学会(ありすさん主催)にする予定です。西日本にお住まいの方はシムトラ学会でお求めになるのがいいでしょう。その後冬コミで頒布して、終わり次第通販、は在庫次第です。たぶん品切れはしませんが。
std版OTRPの方針について
ひめしです。本日OTRP v14_5をリリースしました。
#OTRPatch v14_5をリリースしました。https://t.co/LnQTvFQWFI
— ひめし (@himeshi_hob) July 8, 2018
・ベースをr8541にアップデート
・コスト金額表示のON/OFF
・セーブデータを無印で読み書きできるようにするオプションを用意#Simutrans
OTRPはもともと本家standardへの統合を目指して開発していましたが、いろいろあって本家統合を放棄したのが昨年の11月末のことでした。その後は専用配布ページをオープンし(といってもgithubのリポジトリにmarkdownを置いただけですが)、ほそぼそと修正アップデートを重ね、さらにsimutrans extendedへの移植を行い、今年6月にextendedへの統合を果たしました。
extendedへの統合を果たしたからといって全てが終わったわけではありません。OTRPには依然として不完全な箇所が多く存在しており、これからもそれが顕在化するごとに一つ一つ対処していくことになります。extendedに統合をしていただいたからこそ、私には保守を続ける責任があります。
ところで、最近のOTRPは純粋な「二車線パッチ」の機能以外にも様々な機能を搭載するようになりました。height制限を64に拡張したり、交差点一時停止(結局コレは失敗作に終わりましたが)を実装してみたり、道路信号の方向制御を実装したりといった具合です。現在の(standard版)OTRPはこのように独自機能を拡大しています。
するとOTRPは私、ひめしが運営する本家simutransの拡張版ということになるのですが、コレだけ聞くとjamespetts氏のsimutrans-extendedと同じことをやってるように聞こえます。本体改造にとって配布チャンネルはまとまっていればまとまっているほど良いので、私がやっているOTRPプロジェクトは全てextendedに一本化すれば良いという話になります。
しかし、私はそうはせず、今後も独自に「simutransひめしエディション」の開発・配布を続けていくつもりです。というのも、extendedはもはやstandardとは別ゲーであり、セーブデータの互換性もほぼなく、standardが好き・慣れている人々にとって到底受け入れられるものではないからです。
そこで「simutransひめしエディション」では以下を開発の原則にしています。
- アップデート時は常にsimutrans standard nightlyに追従する。あくまでもstandardの拡張版であり、本家でされた改良、バグ修正の恩恵をプレイヤーが確実に受け取れるようにする。アップデートで必ずnightlyに追従することで本家の変更をmergeすることが困難になるのを防ぐ。
- 独自アドオンを要求する機能は搭載しない。standard用のアドオンがあればそれで完結する設計にする。ただでさえ64, 128, extended用のpakとフィールドが分かれているsimutransのアドオン界隈において、OTRP独自の規格を設けてもうまくいくはずがないからである。
- セーブデータの互換性を確保する。いつ何時も、standardのセーブデータは必ずOTRPで読み込めるようにする。standardのセーブデータを読み込んだときは、OTRPで開いたときにはstandardと全く同じ挙動をするようにする。(つまりOTRPはただのstandardとしても使える。)OTRPのセーブデータもOTRP独自の機能まわりを犠牲にすればstandardで読み込む手段を提供する。(v14_5で実装済み)
- UIの互換性に配慮する。standardからOTRPに乗り換えてきた人にとって、OTRPの機能について何も知らなくてもstandardとして遊ぶ分には困らないようにする。プレイヤーはOTRPの機能について少しずつ探求することができる。(extendedは始める前にある程度extended独自のゲームシステムについての知識がないと不自由する傾向にある。)
すなはち、「便利になったsimutrans standard」を目指すという話です。standardで遊ぶならOTRPで遊んだほうがよい、そう皆さまに思っていただけるようなプロジェクトにしたいと思っています。
あくまでOTRPはstandardの拡張であり続けるつもりなので、OTRPで追加した機能は必要に応じてextendedにpull requestしていきます。extendedにはextendedの設計思想があるので、これに合致すると判断した機能のみextendedに持っていくことになります。(例えば交差点一時停止は今のアルゴリズムではダメ。)
ちなみにバージョン番号の付け方ですが、v12以降はセーブデータの構造が変わったときにmajor number(v14_5の14の部分)を上げることにしています。データの構造が変わらないアップデートのときはminor numberが上がります。セーブデータに記録されているのは実はmajor numberだけです。
OTRPは常に皆さまのためのsimutransでありたいと思っています。どんな些細なことでもtwitterで #OTRPatch のタグをつけて投げていただければ可能な限り対応いたします。OTRPのコード自体の信頼性がどうしても本家standardより大きく劣るので皆さまにご迷惑をおかけすることも多いと思いますが、できる限りサポートさせていただいて信頼していただけるプロジェクトにしていく所存です。
今年の夏はいろいろと忙しいので細々とやっていくことになりそうですが、今後ともよろしくおねがいします。
2017年をふりかえる
年の瀬ということで2017年のシムトラ的活動を振り返ろうと思います。
2016年12月〜2017年1月
シムトラ本体いじりをいつから始めたかは定かではありませんが、2016年の年末には既にいじっていたのは覚えています。最初はTiny Timetable Patchの移植でもやろうかなと思いましたが難しそうなので「追い越し条件を緩和するだけでできそう!」という理由でOTRP(二車線パッチ)の開発をはじめました。
その後大学の期末試験のおべんきょうをいくらか犠牲にしながら開発は進み、プロトタイプを公開するに至りました。
2月〜3月
大学の春休みになったのでOTRPの開発が一気に進みます。2月6日にversion 1をpost。その後ハイペースで開発は進み、現在のおおよその骨格ができあがります。(UIまわりはしばらく紆余曲折)
4月〜7月
基本的には大学の講義やレポンヨヨに忙殺されていたのでOTRPの開発は停滞します。(が、なんだかんだいってちょくちょくはやっていました。)LCパッチを手直しして本家に投げたのはこの時期ですね。
5月には横浜で第一回シムトラ学会が開催され、OTRPを始めとしたパッチ開発の普及を図りましたが、元はといえば私のこんなツイットがきっかけでした。
みなさん各々アドオン制作や都市開発のノウハウなどたくさん蓄積されてると思いますのでそれをサクッと発表するLTオフ会とかやってもいいのかもしれない #Simutrans
— ひめし (@himeshi_hob) 2017年4月3日
3月に大学の学科でLT(ライトニング・トーク。一人5分程度の持ち時間で次々とプレゼンをしていく。)合宿をしたのがきっかけで、それをシムトラ界隈に持ち込もうと思ったわけです。
8月〜9月
OTRPは増築を繰り返した影響でバグ(仕様上の欠陥?)が際限なく増えていったのでひたすらその対処をし続けていました。あいかわらずバグだらけのコードのおかげで本家フォーラムでの議論も陰りが見え始めます。
この頃になると改造したいポイントが増え、一人ではどうしようもないレベルになってきました。そのうち私が手を付けることができたものについてはこのあいだのアドベントカレンダーに書きました。
コードをいじれる人、そうでなくてもプログラミング的思考ができる日本勢が大幅に不足している現状を見て、少しでも状況を改善しようと実践パッチ開発ゼミを開催したのもこの頃ですね。9月のおわりには札幌シムトラ学会もありました。
10月〜12月
学期中はやっぱり大学に忙殺されるのでシムトラの活動は止まります。
OTRPは9月の札幌学会の頃には実用上致命的な欠陥が取り除かれた状態になりました。しかし依然としてコードは整理されず、自分の開発リソースも限られているのでstandardでの開発を諦め(どういうかたちであれ)新しい進路をとることを決断しました。
This patch has not been updated for over a month, and I decided to suspend the development.
Still I continue to distribute the binaries and follow the changes of the master brunch, but I cannot go further.
If there is someone who want this feature in simutrans standard, it's someone in this forum that completes this patch, not me.
学科でアドベントカレンダーが開催されていたのに刺激されて、シムトラ界隈でもアドベントカレンダーを持ち込んでみました。最終的に全枠埋まり、非常に密度の濃い記事が多数投稿されたことは主催としてうれしいかぎりです。
まとめ的なにか
個人としては2017年はただの1プレーヤーから1 contributorへと立場が変わった年でした。contributorというかproject participantといったほうがいいかもですね。
間違いなく人生で最もシムトラ漬けな一年ではありましたが、最初一人で作っていたパッチから最終的にはこうして界隈のみなさんと共に歩んでいく方向に少しは行けたかなと思っています。一人でやれることはあまり多くありません。自分でやるより界隈のみなさんと一緒にやっていけたらなという思いからシムトラ学会を提唱し、パッチゼミを行い、アドベントカレンダーでより多くのみなさんと深いレベルの議論ができるように活動してきたつもりです。
年末のシムトラ学会で今後の指針てきなものを打ち出したっきりそのままになってますので年明けから当面はその指針に沿って行けたらと思っています。2018年の抱負的なものは特に考えていませんが、しいて言えば指針の完遂ということになるでしょう。
2018年は4月から研究室に配属され8月には院試もあるのでシムトラから離れるかもしれません。2018年もシムトラがよりすばらしい「なにか」になってくれればいいなと思っています。くれればいいなじゃないですね。これをお読みのみなさんが各々活動するんですね。
乱文失礼しました。よいお年を。