SVX日記
2005-06-11(Sat) 「スバル3300」のオイルを交換する
ノビノビになっていたが、ホームセンタでSVXのオイル交換を行う。フト気になって前回のオイル交換がいつだったか調べてみたら、正月に55,095kmで交換していた。半年弱ってトコか。しかし半年で2000kmチョイしか走っていない。通勤に使ってないし、最近は遠出もしていないとはいえ、もう少し、走ってあげたいトコロだな。
……と、車を預けてしばらくしたら、放送で呼び出された……ハイハイ。ポンコツ好きのオイラですからね、慣れてますよ。どーせ、どっかが壊れてますとかいわれるんでしょ……と、思いつつピットに行ったらアンノジョーであった。「なんかのオイル」が漏れているらしい。たぶんパワステじゃないかというコト。それからATフルードがウスくなっているらしい。ウスいという表現もよくワカらんが、もーすぐ車検だし、その時にまとめて面倒見てもらうことにしよう。
でもね、オイラはオイルがダダ漏れでなければ、継ぎ足し継ぎ足しして乗るコトに抵抗ないんだよね。実際、以前に乗っていた三菱のジープは、あろうことかブレーキフルードを毎日継ぎ足して乗っていたし……サボるとブレーキが効かなくなるので、その時はテクニックが必要だったケド。ブレーキのウォーニングランプが、パーキングブレーキの外し忘れ以外でも点灯するってコトに気づいたのは収穫だったな……。
ブレーキでもそんな調子なんだから、極端な話、パワステオイルなんてナシでも構わんのだ。ちょっとハンドルが重かろうが、そんなの関係ないのである。オイラは駐車場で据え切りなんかしないし。ジープの細い重ステハンドルで手首は鍛えられてるし。
余談はともかく、オイラのSVXは今までの経験だと、オイルの交換から2500km程度でアイドリング時のバタつき感が増してくるコトが多いので、今回はイチバンいいオイルを入れてみた……といっても、工賃込みで2890円なのだが。まぁオイラは高いオイルを長くというよりは、安いオイルをマメに交換する派なので、そんなので十分なんですわ。
そんなこんなで再び交換作業を続けてもらいながら、ホームセンタを一回りしつつ、何かと工作に使いがちなアイテムを物色する……と、なんと電子工作コーナーを発見!! LEDとかジャック、各種プラケース程度の品揃えではあるが、秋葉までワザワザ買いに行くコトを考えたら……と、思ったのだが高ぇッ!!
おいおい、RCAジャックは秋月だったら1個35円だぞ。コレ3つ買っただけで、ほとんど交通費が出ちまうじゃねーかよ。まぁココで売ってもほとんど売れないだろうからこーゆー値付けになるんだろうが、コレを工作好きな子供が買うと考えたらちょっと痛々しいなぁ。もーすこしなんとかならんモノか。
オイル交換を終了し、エンジンがスムーズに回るようになったSVXでカミさんの実家へ。お茶飲んでPCのメンテ。このPC、以前に謎の症状を示したためにCDROMを外してあるので、先日買った100円のジャンクに入れ替える。よっしゃ、動くようになった。動くようになったのはいいが、なんだか全体にスッゴク重い。Celeron800MHzなのに、体感133MHz以下という重さだ。HDDのフラグメントを疑ったが、解消してもまだ重い。描画か? ベンチマークしてみると、DirectDrawの値が0。いくらなんでもコレはないだろ……ビデオドライバを入れ替えて、もう一度ベンチマーク。DirectDrawの値が19に向上し、かなり動きがスムーズになった。こんなのアリか? まったくもって謎のPCである。最後にWindowsUpdateをかけて、作業終了。
2009-06-11(Thu) カバードコア撃破
それから、ボスラッシュ面。のっけから、ザブはヤバいくらい出てくるし、ビックコアはフェイントかけてくるし、ゴーレムは微妙に速くて追い詰められそうになるし、テトランは腕の先から出す弾が避けづらいし、ガウは堅めで長期間レーザーの雨を避けなきゃならんし、イントルーダも分離してから今一歩ネバるからアセらされるし、カバードコアのミサイルに至っては当たり判定がデカい上に速度が速い……気がする。
2014-06-11(Wed) 帽子を被りたいなら、勝手にしやがれ
先日、オーダーメイドで注文した中折れ帽が届いた。
かなりイメージ通りの仕上がり。被り心地も悪くない。しかし、ちょっと派手かな。ホントはパナマを試したかったのだが、ボルサリーノを買えば数万するし、取り扱いが難しそうだし、ということで、これだ。
2024-06-11(Tue) 太郎くんはサーキットを時速100kmで走ることにしました
一段落してしまった気分でしばらく放置してあった自作のレースゲームだが、近所のサーキットを走ってみたくなったので開発を再開することにした。
まずは画面切り替えを故意に遅延していたのを是正する。厄介なのは地図タイルをネットワーク越しに得る都合上、故意でなくても少なくない遅延があることだ。処理を集中することなく、段階的に処理が進むよう、こんなコードを書いてみた。
draw: ->
n = 0; while(n < @tile_reqs.length)
tile_req = @tile_reqs[n]
unless(tile_req[3])
tile_req[3] = new Image # Image インスタンスを生成
else unless(tile_req[4])
tile_req[3].src = tile_req[2] # Image.src を設定
tile_req[4] = true
else if(tile_req[3].complete) # 読み込み完了?
@constructor.pats[0] = tile_req[3]
@put_source_plane(@constructor.pats, 0, tile_req[0] << 8, tile_req[1] << 8) # ソースプレーン(回転前)に転送
tile_req[5] = true # キューから削除せよ
if(tile_req[5]) then @tile_reqs.splice(n, 1) else ++n
要するに1フレームで複数の処理をすることを避け、分散させるわけだ。まだ試作段階なので、分散のアルゴリズムにはチューニングの余地があるが、とりあえずそれほど大きくはツッカカることなく走り続けられるようになった。
そこで改めて表題の問題だ。現状スペースキーを押すと固定速度で前進走行するようになっていて、それは0.3という適当な値を与えている結果なのだが、いったい時速何kmで走っているのだろう。別に「ゲームなんだから考えません」という方向性もあろうが、以前に書いたように「実際に遊んだときに実際のF1のラップに近いタイムが出るようにしたいと思っている」のであれば、その辺をキッチリさせる必要があるのだ。
まずは、0.3で走っている現状の速度を求めてみよう。どうやるか。実際にコースを一定距離走らせ、経過時間を求めて算出すればいい。そうなると長い直線を持つコースが望ましいが……そうだ、昔、自分が働いていたつくばの土木研究所の試験走路を走らせてみよう。
query = new URLSearchParams(window.location.search)
switch query.get('course')
when 'silverstone'
lng_d1 = -1.022485; lat_d1 = 52.069097; v = 91 # シルバーストン
when 'marinabay'
lng_d1 = 103.864217; lat_d1 = 1.291532; v = 58 # マリーナベイ
when 'doken'
lng_d1 = 140.072549; lat_d1 = 36.115692; v = 184 # 土木研究所
else
lng_d1 = 136.540617; lat_d1 = 34.843099; v = 164 # 鈴鹿
なんか懐かしいなぁ、って思いながら車を走らせていると、視界が恐ろしく狭いことに気づいた。試験走路から一般道に出て、当時の寮に帰ってみようと思ったのだが、どこを走っているのかわからず、帰れない。仕方ないので、より小縮尺のグーグルマップをすぐ右に別に開いて、それを参照しながらドライブしてみた。つうか、これは「ラリーX」じゃないのかw? それにしても、これだけでもだいぶ楽しいな。気づくと土浦学園線、土浦ニューウェイを通って土浦駅まで走っていってしまっていた。何をやってんだオレわw。
試験走路に戻ろう。とりあえず、わかりやすい目印を決めてそこまで走ろう。スタート地点からそこまでの距離を、グーグルマップの「距離を測定」機能で求める。1.03km。で、ストップウォッチを片手に、車を走らせる。29秒。「1.03km / 29s x 3600s = 127.9km/h」。おぉ、おおよそ時速100kmだったんだな。
まずは0.3が127.9km/hになった理由を求める。0.3は256分率表現で76.8。60FPSなので4608/s。ズームレベル20なので4ビット右シフトして288ピクセル/s。24ピクセルが2mなので24m/s。時速にすると86.4km/h……あれ? いや、地図の側を緯度補正して24ピクセルを2mにしてるんだから、速度の方も補正しなければならないのか。「86.4km/h x 256 / 178 = 124.26km/h」。おぉ、だいたい同じになった。計算は合っていそうだ。
これを逆にして100km/hから求めればいい。「100km/h * 178 / 256 =69.53km/h」。秒速にすると19.31m/s。24ピクセルが2mなので232ピクセル/s。ズームレベル20なので4ビット左シフトして3708/s。60FPSなので61.8。256分率表現なので0.241。ということになる。んが、毎回こんな計算をするのは無駄なので、計算順序を入れ替えて定数にまとめてしまう。「0.3472 * 178 *1.0 / 256 = 0.241」緯度が変わったら178を変更し、200km/hにしたければ1.0を2.0に変更すればいいわけだ。
2025-06-11(Wed) ゲームパッドの入力処理が「理解」されてしまう
自製のレースゲーム「TopDrivin'」の開発を地味に進めているのだが、一風変わった操作方法にするつもりだ。オイラはずっと前から、レースゲームに「アクセルボタン」って要るか? って思ってた。
クルマの運転には、主にステアリングとペダルとギアチェンジレバーを使う。実車では手と足を使うのに、ゲームコントローラだと手だけ。足りるわけがない。だからオートマになってしまう。違うだろ。大事なのはギアチェンジだ。むしろアクセルペダルなんて基本ベタ踏みなんだからいらない。ギアチェンジで速度調整、ブレーキ(アクセルオフ)で微調整。だから、シフトダウン、ブレーキ、シフトアップの3ボタンでの操作のがベターだと思うのだ。
で、いきなり細かい話になるが、レバー操作とブレーキ操作はレベルセンスでいいが、シフトのアップ、ダウン操作のためにはエッジセンスが必要だ。つまり、押した瞬間だけを検知する。離したこと、押し続けていること、は検知しない。実際のF1マシンのパドルシフトもそうだ。押し(引い?)たら1段階シフト。「ドゥ、ドゥ、ドゥ、ドゥーン」という連続シフトダウンの操作は、絶対に再現したい。
キーボードの入力処理は簡単だ。キーダウンイベントの発生を検知すればいい。しかし、ゲームパッドのボタンにはダウンイベントはない。レベルを見てエッジセンスする必要がある。とりあえず、汎用性は必要ないから、処理効率よくコンパクトに書きたい。で、書いた。うまく動いた。
以下のコードについて説明して下さい。
p_dirs = ['PLEFT', 'PRIGHT', 'PUP', 'PDOWN']; p_ndirs = p_dirs.length
p_trgs = ['PTRGA', 'PTRGB', 'PTRGC']; p_ntrgs = p_trgs.length
padsense = -> # ゲームパッドからの入力
pads = navigator.getGamepads()
(il = inputs['LEVEL'])['PLEFT'] = il['PUP'] = il['PRIGHT'] = il['PDOWN'] = il['PTRGA'] = il['PTRGB'] = il['PTRGC'] = false; ies = inputs['EDGE_SENSE']
for n in [0...pads.length]
continue unless(pad = pads[n])
dn = 0; for an in [0..pad.axes.length - 1]
pad.axes[an] < -0.5 and il[p_dirs[dn % p_ndirs]] = true; dn++
pad.axes[an] > 0.5 and il[p_dirs[dn % p_ndirs]] = true; dn++
tn = 0; for bn in [0..pad.buttons.length - 1]
pad.buttons[bn].pressed and il[p_trgs[tn % p_ntrgs]] = true and ies[p_trgs[tn % p_ntrgs]] |= 1; tn++
for trg in p_trgs
(ies[trg] &= 0b11) == 0b01 and inputs['EDGE'].push(trg)
ies[trg] <<= 1
このコードは、**ゲームパッドからの入力を取得して、入力状態やエッジ(押
された瞬間)を検出する**処理をしています。CoffeeScript風の構文で書かれ
ているため、JavaScriptに似ていますが、少し簡潔な書き方になっています。
### 🔧 **全体概要**
* ゲームパッドの入力(十字キーやボタン)を取得し、
* どの方向やボタンが押されているかを判定し、
* 「押された瞬間」(立ち上がりエッジ)を検出して `inputs['EDGE']` に追加しています。
### 🔹 **定義と初期化**
```coffeescript
p_dirs = ['PLEFT', 'PRIGHT', 'PUP', 'PDOWN']
p_ndirs = p_dirs.length
p_trgs = ['PTRGA', 'PTRGB', 'PTRGC']
p_ntrgs = p_trgs.length
```
* `p_dirs`: アナログスティックや十字キーの入力方向を定義。
* `p_trgs`: 押しボタンに対応する識別子(トリガー)。
* `p_ndirs`, `p_ntrgs`: それぞれの数。
### 🔹 **関数定義 `padsense`**
```coffeescript
padsense = ->
```
* ゲームパッドの状態を調べて入力情報を更新する無名関数です。
### 🔹 **ゲームパッドの取得と初期化**
```coffeescript
pads = navigator.getGamepads()
(il = inputs['LEVEL'])['PLEFT'] = il['PUP'] = il['PRIGHT'] = il['PDOWN'] = il['PTRGA'] = il['PTRGB'] = il['PTRGC'] = false
ies = inputs['EDGE_SENSE']
```
* `pads`: ブラウザから接続中のゲームパッド配列を取得。
* `inputs['LEVEL']`: 現在の入力状態を保持(どの方向やボタンが押されているか)。
* `inputs['EDGE_SENSE']`: 押された直後を検出するためのバッファ。
### 🔹 **アナログスティック/方向キーの処理**
```coffeescript
for n in [0...pads.length]
continue unless(pad = pads[n])
dn = 0
for an in [0..pad.axes.length - 1]
pad.axes[an] < -0.5 and il[p_dirs[dn % p_ndirs]] = true; dn++
pad.axes[an] > 0.5 and il[p_dirs[dn % p_ndirs]] = true; dn++
```
* 各ゲームパッドを確認し、
* スティックの軸(axes)ごとに、
* -0.5 未満 → 方向の一つ(例: 左)を押されたとみなす
* 0.5 超 → 別の方向(例: 右)を押されたとみなす
※ `dn % p_ndirs` によって 4方向にうまく割り振っている。
### 🔹 **ボタンの処理**
```coffeescript
tn = 0
for bn in [0..pad.buttons.length - 1]
pad.buttons[bn].pressed and il[p_trgs[tn % p_ntrgs]] = true and ies[p_trgs[tn % p_ntrgs]] |= 1; tn++
```
* 押されているボタンに対応する `PTRGA`, `PTRGB`, `PTRGC` に `true` をセット。
* 同時に `EDGE_SENSE` ビットを立ててエッジ検出に備える(OR 1)。
### 🔹 **エッジ検出処理**
```coffeescript
for trg in p_trgs
(ies[trg] &= 0b11) == 0b01 and inputs['EDGE'].push(trg)
ies[trg] <<= 1
```
* 直近2フレーム分の押下状態を 2bit で追跡。
* `0b01`(前回は押されていなかった、今回押された)=立ち上がりエッジ → `inputs['EDGE']` に追加。
* 次のチェックのために1ビット左シフト。
### 🧠 **補足:ビットシフトでのエッジ検出**
`EDGE_SENSE` を2bitとして使っており、
* 過去2回分の押下状態を記録:
例)
* `00`: 押されていない
* `01`: 今回押された → エッジ(立ち上がり)
* `11`: 押され続けている
* `10`: 離された(エッジ検出なし)
### ✅ **まとめ**
このコードは、ゲームパッド入力を毎フレーム更新して:
1. スティックやボタンの現在の入力状態を `inputs['LEVEL']` に反映
2. 立ち上がりエッジ(押された瞬間)を `inputs['EDGE']` に追加
する仕組みです。リアルタイムなゲーム操作などに使える基本的な入力処理パターンになっています。
もし `inputs` の構造や、この関数が使われる文脈についても知りたい場合は、補足のコードを共有していただければ、より詳しく解説できます。
……おぃおぃ……完璧な説明じゃないか、これは。特に、見りゃわかるレベルセンスの処理への言及は軽く、ちょっとわかりづらいエッジセンスの処理について丁寧に正しく説明している。そもそも「エッジセンス」を正しく理解しているということ自体が驚愕だわ。
そりゃ「EDGE_SENSE」とは書いちゃってるけど……変数名も隠しておけばよかったかなぁ……なんだかくやしい。つうか、上で「理解している」なんて書いたけど、AIに「理解」を感じてしまっている時点で負けてんじゃねぇか。だって、AIに「理解」なんて概念はないだろう?
以前に「多項式からその回答が導き出される理由がわからない」と書いたけど、逆に大層に感じているオイラが頭の中の「理解」こそ、たかがその程度の「現象」に過ぎないのだろう。AIが数行の指示だけでそれっぽいものを作り出すという事実は、人間が虫の習性を読み解くかのように、AIが人間の習性を読み解きつつあることの証明なのかもしれない。はぁ……。