Last-modified: Mon, 03 Jan 2005 00:15:01 JST
[dynamic,cache:on]
powered by tds-1.6.2
10:30 起床
書類とメール片づけ。
年末に届いたクローズドβテスト用のアカウントを使って、 ちょっと遊んでみる。
静止画の見栄えは良いが、 マウスでの操作性、 カメラ・コリジョンなどの挙動がダメダメだ。 あまり参考にならない気がしてきた。
NetworkSolutions で private registration サービスを購入。 ドメイン名保持者は保持者に関する情報 (電話番号・住所・メールアドレスなど) を whois データベースに登録して公開する義務があるが、 そこに個人情報を直接書く代わりに Network Solution 社が提供する連絡先を載せておき、 郵便物やメールがあったら実際のアドレスに転送してもらうサービス。
The C++ Standard は 2003 年に改訂された C++ 標準規格 (ISO/IEC 14882:2003) に対応した英国版の規格書。 1998 年版の ANSI C++ 規格書 (ISO/IEC 14882:1998) は ANSI Electric Standards Store で PDF 版を購入したのだけど、ISO/IEC 14882:2003 は $281 と高額、 しかも栞が省かれているとのことで購入見送り。
後者は Tips 集として読めるようなので軽く目を通してみることに。 役に立つ話があれば覚書としてここに書く予定 (ダメなら黙って焚きます)。
のだめ。
後者は、 以前に買った Refrain of Evangelion が仕事中の BGM としてそれなりに使えてるため。
休日だからか 20 分待ち。 次からは、また平日の昼間に行くことにしよう。
ビーフ, 虚空50, ライス大盛り, スープ大盛り。
09:30 起床、10:30 出社。
開発用 PC 不調につき調査依頼を出すも、解決せず。明日に持ち越し。 再現性が低いが致命的なマシントラブルは嫌だ。
リネージュ II の操作性に関して、 プライベートで MMORPG を遊んでいる人間と話す。 どうもリネージュ II は、 元々 2D 系のシステムだったのを 3D に持っていって失敗してる感じ。 確かに私が感じた問題点も、 カメラ回転角固定・俯瞰気味の画面構成なら何ら問題ない。
しかし 3D とマウス操作は相性悪いのかもしれず。 マウスで地面をクリックしてそこに向かって歩くというシステムは、 画面を見下ろすタイプの視点・プレイヤーのビルボード描画と相性が良い。 しかし 3D でプレイヤーを描く場合は、
と、デザイン・操作性の二律相反に陥る。 このあたり FF XI とかどうしてるんだろう。
Hierarchical State Machine の機能拡張 (履歴対応) とユニットテスト書き。 特に問題なく。
Unicode の件引き続き。 Unicode Normalization Forms の話が深すぎ。 ある程度は深入りせざるを得ないのだけど、 既存のプログラミング言語やツール類 (InfoPath など) の対応はどんなものやら。
Perl 5.8 における文字コードの扱いに関して調査。 use encoding で全て解決しそう。 標準入出力やオープンするファイル単位でエンコーディングを指定したり、 ソースコードに用いる文字コードを個別に指定できる。 お手軽だ。
実機上での文字の扱いや、エフェクトシステムの仕様に関して担当者と問題点洗い出し。 いくつか仕事「内容」ではなく「進め方」を軌道修正してもらう。
21:50。 コードを書いてるより、マシントラブルに対応したり、人と話してる時間の方が長い一日だった。
09:30 起床, 10:30 出社。
サブプログラマのサポート&私が書いてるシステム部分ならびにコリジョン関係の概要解説。 これでようやく下準備が終わって具体的な仕事を振れる。 まずは作業見積もりを出してもらう。
現状のコードを Hierarchical State Machine に移行中。 構造上の問題があり、残念ながら単純置換で終了というわけにはいかず。 数日かかりそう。
先週末の開発機材が動かない件。 機材ごと上長に丸投げ……したら別の仕事が降って来るという罠。 面白そうな話だから、やるけど。
21:30
09:00 起床, 10:00 出社。
引き続き現状のコードを Hierarchical State Machine に移行。 明日中には現状の機能を再現するところまでは終わり、 新規機能の実装に入れそう。
HSM であるシグナルが状態遷移を引き起こした場合、 そのシグナルの情報を次の状態の入場処理で使いたい場合がある。 どうやって引き渡すのがスマートだろう。
まだ悩み中。
InfoPath に関して情報集め。 書籍は洋書だといくつかみつけたけど、邦訳まだの模様。 洋書は読んでる時間が勿体ないので、邦訳が出次第、確保する方向で。
機材交換やらプロジェクトで使ってる CGI のメンテナンスやら。
21:30.
09:30 起床, 10:30 出社。
単に「どうだった」と聞くと、 多くの場合「まぁ悪くないよ」程度の答えが返ってくるが、 「もし自分で金を出して参加したとしたら、どう?」と聞くと非常にシビアな意見が帰ってくる。
そういうものだろう。
ようやく完了。よしよし。
ディレクターと「プロジェクト立ち上げ時と比べるとだいぶ人が増えたね」としみじみ辺りを見回しつつ、 進捗報告。
今回のプロジェクトではソースコードのバージョン管理に CVS を用いており、 ソースコードの修正をプロジェクトに公開する (commit する) と、 プログラマに変更内容がメールで通知されるようになっている。 これによってプログラマチーム内部ではかなりの程度まで進捗をお互い把握できているので、 ミーティングではそこで足りない部分、 具体的には各自が詰まっていたり、 自分の仕事を進める上で新たに他の人の対応が必要となった部分について摺り合わせを行うのと、 プログラマチーム以外のプロジェクト全体に関する報告が主。 効率的に話が進んで良い感じ。
もっとも、 個々人の作業割り振りの調整や問題点の早期洗い出しのために、 プロジェクトに初期からいるメンバーはミーティングの場を待たずに立ち回ることになるけど、 それもまたお仕事。
思考ルーチンの実装方法を変更。 これまでは共通の状態遷移を管理するクラス (仮に StateBaseとしよう) にいくつか思考に関連する純粋仮想関数を定義しておき、 それをプレイヤー・CPU それぞれに対応する派生クラスで実装していた。 いわゆるテンプレートメソッドパターン。
ただ、これは間に合わせで作った仕様で、 実際には次のような問題がある。
特殊なキャラクタを実装する場合には、 スクリプトでの挙動操作に留まらず、 C++ ソースコードに手を入れて状態遷移自体をカスタマイズする可能性がある。 私が実装した Hierarchical State Machine では、 状態をメンバ関数ポインタで表現しているので、 状態遷移のカスタマイズは次のように実現するのが低コスト。
ところが、 そうなると StateBase の派生クラスで「思考ルーチン」「状態遷移」を両方実装することになり、 派生クラス数が「思考ルーチン」と「状態遷移」の積になってしまう。
TypeList とクラステンプレートを使った MixIn を組み合わせれば、 手作業で書くのは「思考ルーチン」と「状態遷移」の和で済む *1が、 よくよく考えると「思考ルーチン」を StateBase に値として渡し、 StateBase のメンバ変数として保持、 必要に応じて StateBase が「思考ルーチン」に意見を聞きに行けば良いだけ。 こうしておけば特殊な状態遷移を持つクラスに、 専用の思考ルーチンを渡すのも簡単だし。
そこまで考えがまとまったところで、 一気に書き直し。 どうにか終わり。
ライブラリチームの人に、 「注文は多いのだけど何をやってるか謎のプロジェクト」と思われてるらしい。 前半はたぶん私のせいだ。
それはともかく、後半は何か対処した方が良いかな。
21:50.
本題から逸れるし 古い話 へのコメントになりますが、気になったので。
話がややこしくなっているのは、 コンピュータにおける表現・操作と、 数学上の概念・演算を混同してるためだと思います。
C 言語で c = a * b と書いても、実際にはコンピュータは a と b の数値、 それから掛け算という演算を理解した上で、a, b の積を計算しているわけ ではありません。 コンピュータがやっていることは、単に特定のビット列と特定のビット列を 入力として受け取り、それを論理回路を通して別のビット列に変換している だけ。
ビット列に対して「それは整数の 1 だ」と数学上の概念を関連づけた り、ビット操作を「掛け算だ」と解釈するのは人間側がやっていること。
この点を厳密に区別して考えれば、 代数学の一般論に深入りするほど難しい話とも思われず。
定義 1 表現
ビット列と数を関連づける写像 uint, fixf を次のように定める。
uint(x) ビット列を、符号なし整数として解釈したときの数
fixf(x) ビット列を、固定小数点数 (小数部 8bit) として解釈したときの数
例:
uint('0x00000000') = 0 fixf('0x00000000') = 0
uint('0x00000100') = 256 fixf('0x00000100') = 1.0
uint('0x00000001') = 1 fixf('0x00000001') = 1/256
定義 2 操作
1) mul
z = mul(x, y)
2 つのビット列から別のビット列を生成する操作
ただし x, y, z の間には次の関係が成り立つ
uint(z) = uint(x) * uint(y)
2) div
z = div(x, y)
2つのビット列から、別のビット列を生成する操作
ただし x, y, z には次の関係が成り立つ
uint(z) = uint(x) / uint(y)
問題
操作 mul, div を使い、与えられたビット列 x, y から
fixf(z') = fixf(x) * fixf(y)
を満たすビット列 z' を求めよ
回答
ビット列 z を z = mul(x, y) [*1] で定める。演算 mul の定義より
uint(z) = uint(x) * uint(y) [*2]
となる。
任意のビット列 x に対して、
fixf(x) = α(uint(x)) [*3]
uint(x) = β(fixf(x)) [*4]
となるような写像α、βの存在を仮定する。このとき
uint(x) = β(fixf(x)) = β(α(uint(x))) = (β・α)(uint(x))
よりβ・αは恒等変換。
uint(z') を x, y の関数として求める。
α(uint(z'))
= fixf(z')
= fixf(x) * fixf(y)
= α(uint(x)) * α(uint(y))
β(α(uint(z'))) = β(α(uint(x)) * α(uint(y))
ここでβ・αは恒等変換であるから
uint(z') = β(α(uint(x)) * α(uint(y))) [*5]
写像α, βを次のように定義すると条件 [*3][*4] を満たす。
α(n) = 1/256 * n
β(m) = 256 * m
これを [*5] に適用する。
uint(z')
= 256 * (1/256 * uint(x) * 1/256 *uint(y))
= (uint(x) * uint(y)) / 256
= uint(z) / 256 (∵ [*2])
z' = div(z, '0x00000100')
z' = div(mul(x, y), '0x00000100') (∵ [*1])
私には式をいじったら答えが出た、みたいに見えて、何をやっているのか良くわからないです。
うーむ。 私がこれで納得してしまうのは、 ああいう作業を日常的にやってるからなのかなぁ。
プログラミングしていると、 計算機が実行している操作とそれに対する解釈は完全に別物だと、 繰り返し思い知らされますよね。
例えばシェーダープログラムを書く場合、 同じ 128bit のデータでも色 (RGBA) と扱うこともあれば、 頂点座標 (XYZW), はたまたグラフィックハードウェアへの命令の場合もある。 その表現と意味の関係づけはプログラマが行うもので、 計算機は何も助けてくれない。
コンパイラ・トランスレータを書く場合も然り。 入力を解析して受理するか否かを決める部分と、 それに対して適切な意味を付与して処理することは全く別。 前半分は lex, yacc が助けてくれるけど、 後半は人間が書くしかない。
uint(z') = β(α(uint(x)) * α(uint(y))) をより一般化したものが αo(fo(α^-1 o A)) = F o A だから、 わざわざ uint, fixf に退化させるのはどうかというのは確かなのですが、 有野さんの記述を読んでいて気になったのは、 その「αo(fo(α^-1 o A)) = F o A」という表記と、 それを人間がどう理解・納得するかの間に「溝」があるのを忘れてないかという点。
代数学にせよ CPU の計算にせよ、 実際には特定のルールに従った記号操作に過ぎなくて、 それをどう解釈するかは人間の役割です。 今回の問題も、 そこで解釈の仕方を間違えた (a, b が固定小数点数にもかかわらず a * b というプログラミング言語の表記で a と b をかけると解釈してしまう) ので話がこじれた、 というのが本質かなと。
もっとも、 この話も突き詰めると
という話になって、 ウィトゲンシュタインの「語り得ぬものについては、沈黙せねばならない」に行き着きそうだけど。
こういう問題が頭にこびりついて離れなくなると、 人間、 哲学やるしかないみたいです。 私は気にならないので、 哲学は趣味で読むぐらいですけど。
そういえば時間の型、 現状 float (秒単位) にしてるけど、 これを固定小数点数 (秒単位) に変更するかな。
| 累計時間 | 1/60 秒 加算時の誤差 (秒) | 誤差率 (%) |
|---|---|---|
| 1分 | 0.00000 | 0.002 |
| 1時間 | 0.00000 | 0.391 |
| 1日 | 0.00104 | 6.25 |
| 1ヶ月 | 0.01667 | 100 |
float だと 1 時間程度で、 1フレーム (NTSC だと約1/60 秒) に対して 0.4% 弱の誤差が出てしまう。
15:15 自宅発, 1km 泳いで 16:25 自宅着。
弟の所に寄って ハイパーストリートファイター II ~アニバーサリー・エディション~ で軽く遊んだ後、 マジスパへ。
ポーク, 虚空50, スープ大盛り, クイティオ大盛り, フィッシュボール。 食後にココナッツアイス。
私はストリートファイター II' ぐらいまでが, 最も足繁くゲームセンターに通ってた時期。 すっかりゲームセンターに行かなくなって久しいけど、 最近 (仕事がらみで) いくつかチェックしておきたいゲームがあるから、 近いうちに出かけよう。
09:00 起床, 10:30 出社。
Unicode を扱う都合で、メインプログラマの環境に xemacs 21.5.16 を導入。 わりと手間取った。 あとは set-buffer-file-coding-system, set-input-method で使用する文字コード、 入力メソッドを適切に設定すれば問題なし。
クラステンプレートとマクロを使って、 汎用のファクトリクラスを実装。 非ローカルスタティックオブジェクトの小細工を使って、 main() 実行直前にインスタンス生成関数をファクトリクラスに自動的に登録させる。
人事関係の件で問い合わせ。 どうも手際が悪いな。
単精度浮動小数点数の誤差に関して注意喚起。
そんな感じで、あまり物事が進まず一日終わり。
21:50.