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|12|
2025|01|02|03|04|05|06|07|

2005-05-01(Sun) 古典名作ゲームの保存について考える

  そんなこんなで、今日はドラクエを求めてカケずり回るのである。が、とりあえず近所のリサイクルショップ……というよりはジャンク屋に掘り出し物があるかもしれないと思い、ちょっぴり寄るのであった。

  画像の説明

  ココは以前から魅力を感じていた場所である。そこで今日はデジカメを持っているし、適当に掘り出し物を漁りつつコッソリ写真を撮ってしまうコトにする。特にココの二階は九龍城かと思うほど、カオスな感じがステキなのである。

  画像の説明

  ガサガサと箱の中を漁っていたらドラクエ2のカートリッジが見つかった。ファミコン用のオリジナルである。おそらく90円で売ってくれるモノとは思うが、ファミコン本体がないとどーしよーもない。できれば、ゲームボーイ用のドラクエが欲しかったのだが。

  画像の説明 画像の説明

  そして二階。もぅ、狙っているとしか思えないようなマネキンの配置である。廃墟好きなヒトには堪らない光景だ。しかしこれはヒトケタ国道沿いの店なのである。イイ。すっげぇイイ。すっげぇイイが特に購入したいアイテムは見つからなかった。さようなら。

  その後、3軒ほど古本屋系の中古ゲームショップを回るが、ドラクエは見つからない。ドラクエは1と2をひっつけたモノがゲームボーイカラー用に出ているらしいトコロまでは調べたのだが、売っていない。ついでに調べたところい、ゲームボーイの系譜は、元祖ゲームボーイ、ゲームボーイカラー、ゲームボーイアドバンス、ニンテンドーDSという進化を遂げており、すでに元祖とカラーはディスコンという扱いらしい。中古屋をみても、ゲームボーイカラー用のソフトは千円以下がメインで裸で転がしてある。

  しかしオイラは単にドラクエをやってみたいのだからして、別に現行の機械を買おうとはサッパリ思わない。むしろゲームボーイカラーは現行から外れているために、妙に安いのが魅力なのだ。千円前後で売っている。これでソフトが2千円以下程度であれば、3千円でドラクエが楽しめるのだが。

  と、ココでふと矛盾に気がついた。家庭用ゲーム機というジャンルが確立して20年、そろそろソフトとハードを分離してもいい頃じゃないか、と思うのだ。遊び捨てるようなソフトはともかく、ドラクエや、ナムコのオールドゲームなんかは、小説でいえば「古典文学」として扱ってもよい後世に残すべき傑作だと思うのだ。

  事実、ナムコのオールドゲームはあらゆるプラットホームに移植されている。ココに矛盾を感じて欲しいのだ。何度も何度もプラットホームが変わるたびに再プログラムせねばならない。そしてその都度、マニアに移植度がどうとか突っつかれるのである。

  それに対する、ひとつの回答はエミュレータである。元来のコードを利用するのだから、移植度が低いわけがない。しかし法律的にグレーなのが痛いし、所詮は有限である各ハードウェア(主にIBMPC)に依存するのが面白くないトコロである。オイラが欲しいのは、ゲームの古典文学を正々堂々、永久に所有し味わうコトのできる権利なのである。

  そして提案するのだ。ハードの性能をギリギリの引き出したソフトはそれとして、もう少しランクの落ちる共通のプラットホームを立ち上げようではないか。この考え方は、その昔Oh!mzという雑誌で提案されたCIOSの考え方とほぼ同じである。CPUがZ80という共通のプラットホームの下に、最低限の共通ルーチンを持たせ、その中では共通のアプリケーションが動くようにするというシステムだ。

  そしてこのゲーム版が「Common-Game-Playing-System」略して「CGPS」だ。CGPSは特定のハードに依存せず、規格として提唱されるコトで、永遠に継続が可能なプラットホームとなるのだ。規格はオープンであるため、規格に準拠すれば特にロイヤリティ不要でハードもソフトも作れるものとしてしまおう。最初の段階ではCGPS-ver.1.0として、拡大縮小可能なスプライトとFM音源相当あたりを必須性能に設定しようか。現実のゲームプラットホームに例えるなら、ナムコのシステム2基板相当というトコロだ。

  望ましい展開としては、ニンテンドーDSとPSPにどーにかしてCGPS-ver.1.0をサポートしてもらいつつ、シャープがザウルスの付加機能として、DoCoMoとAUが携帯電話の付加機能としてサポートするというトコロだろうか。同時にキラーソフトを出すことも忘れてはならない。ナムコからはナムコエターナルシリーズとして1,2,3を同時発売しよう。1,2はいわゆる定番を5本ずつまとめつつ、3には「アサルト」「ワルキューレの伝説」あたりを含めよう。

  セガからは「体感ライブラリ」として「スペースハリアー」「アウトラン」「アフターバーナ」「パワードリフト」「ギャラクシーフォース」の5本組みを出そう。コナミからは「グラディウスレジェンズ」で全部入りだ。スクエアエニックスからは「ドラクエトリオ」と「ファイナルファンタジートリオ」の発売だ。カプコンなんかも「ストリートファイターズ」で全部入り。データーイーストも「デコシューチョイセズ」と「デコジャンプチョイセズ」を出してしまおう。他にも「オールタイプアールタイプス」「コンプリートダライアセズ」など……うぉぉ!! 買うぜ!! 全部買うぜッ!!

  1年後にはCGPS-ver.2.0を発表してポリゴンに関する必須性能を設定、さらに多くのゲームを取り込めるようにする。その頃には、オリジナルのソフトも出だすから、ゲームプラットホームの第三勢力として確固たる地位を築いているだろう。オープンの強みである。

  というワケで、業界のブランドであるナムコあたりに牽引役になってもらって、ゼヒ実現して欲しい。オイラの望みはただひとつ「ゲームの古典文学を永遠に手元に置いておきたい」ダケなのだだだッ!!

  画像の説明

  <かきかけ>


2008-05-01(Thu) 洗うThinkPad

  連休中、名古屋の実家に帰っていた。ガキを新幹線に乗せるので、車中においてオイラのラップトップ(膝の上)は占拠状態になることは想像に難くなく、事実そのとおりになった。PCを持って帰らなくてよかった。ちなみに、行きは500系、帰りはN700だったが、N700の乗り心地の快適さは異次元であった。うぅむ、500系好きにはちょっと悔しいぞ。足元に妖しく光るコンセントもあったしなぁ。

  画像の説明 画像の説明

  連休前にTninkPad用のHDDを探しまくったところ、秋葉の若松で売っているのを発見した。んが、20GBで1万円……奥さんッ!! 20GBで1万円ッ!! ……うげぇ、である。80GBなら……いや、せめて40GBなら……いくらなんでも、20GBではちょっとバカバカしくてやってられん……そだろ?

  一方で、そんな若松にはCFアダプタも売っている。コンパクトフラッシュを挿すと、1.8インチのHDDスロットにHDDとして挿せるというシロモノだ。しかし、最近は安くなったとはいえ、許せる容量のCFが、許せる値段で売っているもんかいな……と、調べたところ16GBで8千円弱からある。奇しくも、2千円弱のCFアダプタと併せると、ちょうど1万円である。

  相手がHDDとなると圧倒的に高価だが、相手が「1.8インチのHDD」となると、検討対象になってしまうというのが、これまた絶妙すぎる巡り合わせといえよう。容量だけ比べれば4GB足りないが、ケタ違いのランダムアクセス性能と、ショック耐性、バッテリ容量への貢献を考えると悪くない。悪くないが、書き換え回数が有限というのは、気分がよろしくないのも確か……

  ……などと、結局、連休中にジワジワと考えつつも、結局、帰りがけに秋葉に寄って、両者を買い揃えてしまった。1980円+7800円。そんなにハッキリと意識したワケではないが、決め手は昨今のフラッシュメディアの値下がりの速度だと思う。SSDが普及域に入ってきた現在、下手をすると、1年後に32GBメディアが1万円弱に降りてきていても不思議ではない。そうなれば、その時点で追加購入、両者を併用すれば48GB。決して贅沢ではないものの、不満を感じるレベルからは脱せよう。一方で、HDDは併用できないから、先はない。

  画像の説明

  つーわけで、若松で買ったCFアダプタのジャンパピンを1.8インチモードにし、HDDスロットに挿した。認識状態は以下のとおり。

[root@raven ~]# fdisk -l /dev/sdb
 
Disk /dev/sdb: 16.2 GB, 16240345088 bytes
255 heads, 63 sectors/track, 1974 cylinders
Units = シリンダ数 of 16065 * 512 = 8225280 bytes
Disk identifier: 0x00000000
 
デバイス Boot      Start         End      Blocks   Id  System
/dev/sdb1   *           1        1975    15859208    c  W95 FAT32 (LBA)
[root@raven ~]# 

  なお、この16.2GBというのは、16GBだと考えるとちょっと得した気分になるものの、16GiBには遠く及ばない容量である。まぁ、ギガオーダになると、ギビ単位との乖離には激しいものがあるので仕方ないともいえるが「メモリ」の一種と考えると不思議な感覚もする。

  ちなみに、即死でないことに一喜したバッテリだが、なんと、USB接続のHDDを付けた状態で、2時間半も稼動した。こりゃ、バッテリ代も浮いたなぁ。

  さて、Fedora9の正式発表まで1週間を切った今日この頃であるが、それを待たず、再度Preview版をフラッシュにインストールしなおして本格稼動を開始してしまおうと考えているセッカチなオイラである。ではまた。


2011-05-01(Sun) 布製ハチロク

  なんでも、ウチのガキが通っている幼稚園は、スモックにオリジナルデザインを施すのが流儀らしい。そういうことなら、アレをデザインしてみようか。適当に拾ってきた画像の上に「文字通り」レイヤを被せる。

  画像の説明

  で、買ったばかりのプリンタで印刷。型紙を作って、フェルトを切り抜く。

  画像の説明

  縫いつけるのはカミさんの仕事。間違いなく「アノ車」である。

  画像の説明

  しかし、幼稚園で頭文字Dにハマってるのは、ウチのガキだけだと思うのだが、これでよかったのだろうか……。


2017-05-01(Mon) お風呂BluetoothスピーカV2とリチウムポリマ充電池対応

  お風呂スピーカの稼働率が高くなるにつれ、電池の交換が面倒くさくなってきた。そんな時は充電池……なのだが、イマドキならば、リチウムポリマ充電池なわけで、でも、それはひとつ間違うと爆弾なわけで。

  そんな気分でAitendoを見ると、ひと通り揃っているんですな、これが。リチウムポリマ電池(3.7V/110mAh)[SX-3.7V-110MAH]と、充電用IC[MCP73831T-2ACI]と、ピッチ変換基板[SOT23-B]。ついでにBluetoothオーディオレシーバ基板[BK3254P9]まで買ってしまう。

  Bluetoothスピーカ化は既に済ませているのだが、USBメモリ形状のものを無理にくっつけているのがイマイチ。たまたま既存の極小MP3モジュール[M2801002]とピンアサインが似ているので、似たような形状に合わせることにした。同じようにチョンチョンと空中配線で2.54mmのピンヘッダにハンダ付けしてモジュール化する。

  画像の説明

  ……が、なんだこれ。電源入れると中国語らしきボイスが流れるんですが……ペアリングにちょっと時間がかかるものの、特に何の操作をしなくてもペアリングされるのはいいが、その時にも中国語らしきボイスが流れる。まぁ、使用には問題がないからいいんだが、ちょっと微妙だ。

  さて、本題はリチウムポリマの方。米粒のようなICをピッチ変換基板に載せる。手配してから気づいたが、充電電流によっては結構発熱するので、できるだけ放熱しやすいピッチ変換基板をチョイスしたほうがいいみたいだ。今回の充電対象は110mAhの容量なので、1Cで100mA程度の電流量であり、たぶん問題なさそうだが。

  ハンダをドバっと乗せてから、はんだ吸い取り線で吸い取る形でハンダ付け完了。ブレッドボード上に周辺回路を組む。たまたま、USBのmicroBの変換基板を買ってあったので、それを経由して充電することに。PCのディスプレイ横のUSB端子から、USB電流モニタを介して、ドキドキしながら充電を開始!

  画像の説明

  PROG端子には、10KΩをつないだので、1時間強で充電完了予定だ。テスタで電池電圧を計測しながら、USB電流モニタを眺めつつ、防爆用(!)のドーム型ルーペを横に、充電状況を見守る。

  基本的な充電戦略は、4.2Vに達するまでは一定電流(今回は100mA)で。4.2Vに達したら、それを超えないよう電流量を減らしていくように電圧を微妙に上下するようだ。実際に計測した充電の経緯は以下。

時刻電池電圧充電電流累計充電量
14:203.90V0.11A0mAh
14:323.95V0.09A24mAh
14:434.00V0.08A40mAh
14:524.04V0.08A53mAh
15:014.08V0.07A66mAh
15:104.13V0.07A76mAh
15:194.19V0.07A88mAh
15:234.19V0.00A92mAh電流値が0に(測定器の問題)
15:304.19V0.00A96mAh
15:434.19V0.00A96mAh
15:484.17V0.00A96mAhLEDが消灯

  ほぼ思った通りの挙動を経て、無事、充電が完了した。

  試しにお風呂スピーカの電源端子につないでみたら、問題なく音が鳴った。単三x3本をこのサイズに置き換えられるのかぁ。これは、ちょっと危険とはいえ、使い倒したくなってくるねぇ。


2023-05-01(Mon) つまらないFactorio

 一番最初のサラ地から遊びたいが製品版を買いたくないなぁ」ということで、5番目の体験版シナリオの既設の施設を全部回収し、サラ地状態にしてから始めることにした。意外と大した手間ではなかった。

  で、改めて、赤色の自動化サイエンスパック、緑色の物流サイエンスパックの量産ラインを完成。机上で苦労して設計したが、狙い通りのラインができた。概ね整然と構築できて満足。ちゃんとつまらないラインは作れるのだな。

  画像の説明

  で、結局、納得してクリアしたところで、遂に製品版を買ってしまう…。


2025-05-01(Thu) デスクトップでF1のラップタイム計測ごっこ

  ラップタイムの計測をしたいなぁ、ということで、実装してみることにした。

  計測には、いわゆる「当たり判定」が必要になる。以前に、2Dシューティングにおける「箱同士」の当たり判定は書いたことがあるのだが、同じ2Dでも任意の角度に回転する物体同士の当たり判定には別の方法が必要になる。

  確かベクトル演算の手法が使えたような。ちょっと前に読んだ線形代数の本を引っ張り出してきて調べる。そうだった、外積だ。座標pが、座標a, bを通る直線のどちら側に位置するのかを判定できる。今回は、コース上のラップ計測ラインの通過を判定したいだけだから、それ一発で済む。まずは、理解するためのサンプルコードを書く。

#!/usr/bin/env ruby
 
# 座標 p が、座標 a, b を通る直線のどちら側に位置するかを調べる
 
def vec(i, j)                                   # 座標 i->j をベクトル化
    {   :x => j[:x] - i[:x],
        :y => j[:y] - i[:y]     }
end
 
def vcross(i, j)                                # ベクトル i, j の外積を求める
    i[:x] * j[:y] - i[:y] * j[:x]
end
 
a = { :x =>  5, :y =>  3 }                      # 座標 a
b = { :x => 15, :y => 10 }                      # 座標 b
ab = vec(a, b)                                  # 直線ベクトル a->b
 
20.times {|y|
    20.times {|x|
        p = { :x =>  x, :y =>  y }              # 座標 p
        ap = vec(a, p)
        print(vcross(ab, ap) < 0 ? ' -' : ' +')
    }
    puts
}

  結果は以下。見事に座標 a, b を通る直線を挟んで、値の正負で判定されている。値が負になったタイミングで、ラップタイムを計測すればいい。

 + - - - - - - - - - - - - - - - - - - -
 + + + - - - - - - - - - - - - - - - - -
 + + + + - - - - - - - - - - - - - - - -
 + + + + + a - - - - - - - - - - - - - -
 + + + + + + + - - - - - - - - - - - - -
 + + + + + + + + - - - - - - - - - - - -
 + + + + + + + + + + - - - - - - - - - -
 + + + + + + + + + + + - - - - - - - - -
 + + + + + + + + + + + + + - - - - - - -
 + + + + + + + + + + + + + + - - - - - -
 + + + + + + + + + + + + + + + b - - - -
 + + + + + + + + + + + + + + + + + - - -
 + + + + + + + + + + + + + + + + + + - -
 + + + + + + + + + + + + + + + + + + + +
 + + + + + + + + + + + + + + + + + + + +
 + + + + + + + + + + + + + + + + + + + +
 + + + + + + + + + + + + + + + + + + + +
 + + + + + + + + + + + + + + + + + + + +
 + + + + + + + + + + + + + + + + + + + +
 + + + + + + + + + + + + + + + + + + + +

  判定処理は毎フレームしこたま繰り返すので、計算を軽くしつつ、オブジェクト化して書き直す。a→bベクトルは一度計算するだけでいいので、TimingLineクラスの初期化時に行うようにし、判定をover?メソッドにまとめる。より、直感的な記述になりつつ、同じ実行結果が得られる。

#!/usr/bin/env ruby
 
# 座標 p が、座標 a, b を通る直線のどちら側に位置するかを調べる
 
class TimingLine
 
    def initialize(a, b)
        @a = a
        @ab = { :x => b[:x] - a[:x],
                :y => b[:y] - a[:y]     }
    end
 
    def over?(p)
        @ab[:x] * (p[:y] - @a[:y]) - @ab[:y] * (p[:x] - @a[:x]) < 0
    end
end
 
a = { :x =>  5, :y =>  3 }                      # 座標 a
b = { :x => 15, :y => 10 }                      # 座標 b
tline = TimingLine.new(a, b)
 
20.times {|y|
    20.times {|x|
        p = { :x =>  x, :y =>  y }              # 座標 p
        print(tline.over?(p) ? ' -' : ' +')
    }
    puts
}

  思索しつつ試作したコードを、CoffeeScriptで書かれたゲームのコードに落とし込む。それなりにオブジェクト化してあるので、どのオブジェクトに、どう落とし込むか、非常に考えどころである。とりあえず、地球上の位置(Wpos)クラス、コース(Course)クラス、メインプログラムに落とし込んでみた。

$ diff ../topdrivin.org/wpos.bean wpos.bean
85a86,92
>   to_vec: (wpos) ->                               # 終点座標を渡し、ベクトル情報を生成
>       @vec_x = wpos.wpx - @wpx
>       @vec_y = wpos.wpy - @wpy
> 
>   vec_over: (wpos) ->                             # 座標を渡し、ベクトル線を超えたか返す
>       @vec_x * (wpos.wpy - @wpy) - @vec_y * (wpos.wpx - @wpx) < 0
> 
$ diff ../topdrivin.org/course.bean course.bean
19c19
<   constructor: (_s, x, y, car) ->
---
>   constructor: (_s, x, y, car, sectors) ->
21a22
>       @sectors = sectors
54a56,58
>       @sector = 0                             # 開始セクタ(ラップタイム計測)
>       @dir = _s['DIRECTOR']
> 
80a85,91
>       # ラップタイム描画
>       @context.font = '24px sans-serif'
>       @context.fillStyle = 'white'
>       @context.textAlign = 'right'
>       @context.fillText(@sectors[@sector]['name'], 100, 24)
>       @context.fillText(@dir.tsc1000, 100, 48)
> 
95a107,108
>       if(@sectors[@sector]['vec'].vec_over(@car.wpos))        # セクタ計測ラインを超えた?
>           @sector = (@sector + 1) % @sectors.length
$ diff ../topdrivin.org/sample_course_view.bean sample_course_view.bean
80a81,88
>               sectors = [
>                   {   l: [25.957223, -80.244210], r: [25.957362, -80.244212], name: 'dummy 05'    },
>                   {   l: [25.956204, -80.243633], r: [25.956089, -80.243657], name: 'sector 1'    },
>                   {   l: [25.958981, -80.229886], r: [25.958922, -80.229795], name: 'dummy 15'    },
>                   {   l: [25.960090, -80.230708], r: [25.960203, -80.230716], name: 'sector 2'    },
>                   {   l: [25.960523, -80.242873], r: [25.960578, -80.243020], name: 'dummy 25'    },
>                   {   l: [25.959922, -80.238718], r: [25.959788, -80.238811], name: 'finish line' },
>               ]
83a92,96
>       @tsc1000 = 0; @tsc1000inc = [17, 16, 17]                # 1/1000時計を初期化
>       for sector in sectors
>           sector['vec'] =      Wpos.deg(sector['l'][1], sector['l'][0])
>           sector['vec'].to_vec(Wpos.deg(sector['r'][1], sector['r'][0]))
> 
87c100
<       @objs['COURSE'].push(new Course(@_s, 0, 0, mycar))
---
>       @objs['COURSE'].push(new Course(@_s, 0, 0, mycar, sectors))
95a109
>       @tsc1000 += @tsc1000inc[tsc % 3]                        # 1/1000時計を加算

  マイアミサーキットの場合、各セクタの計測ラインが、いずれもUターンのようなコーナーの先にあるため、計測ラインの手前にダミーの計測ラインを設けている。そうしないと、各セクタの計測ラインを超える前に、超えたという判定になってしまうためである。いったんアッチに行ってからね、って感じ。

  画像の説明

  ちなみに、計測に使うタイムは、ゲームの固定FPS(1/60タイマ)に同期する仕様とした。ただし、1/60秒は割り切れない値なので、1000分の17, 16, 17を順に加算することで作り出している。これは逆に言うと、60FPSのゲームなので16/1000秒以下の計測粒度はない、ということなのだが、サウジアラビアの予選でポールのフェルスタッペン、ピアストリのタイム差は10/1000秒。それは、優に格ゲーの1フレーム以下の戦いだったってことである。マジかよ……。

  それにしても、今回、最終的に追加したコードは意外なほど少ない。実は丸2日かかっているのだけれど。というのも、上述したように「どのオブジェクトに、どう落とし込むか」が非常に考えどころであり、楽しいところでもあるのである。「ラップタイム計測」をするのは誰(どのオブジェクト)であるべきか? 自車(MyCar)クラスか? 今回はコース(Course)クラスに追加したが、それで正しいのだろうか。今回は、ラップタイムの描画もコース(Course)クラスにやらせているが、ラップタイムの管理も含めて、ラップタイムクラスを新設するべきだし、それを駆動するのは自車(MyCar)クラスであるべきのようにも思える。

  前にも書いたが、やっぱりプログラミングは盆栽だなぁ、と思う。処理をどこに足すべきか。それは、どの枝を伸ばすか、みたいなものなのではないか。間違ったら剪定して、また違う枝を伸ばしてみたり。