HOME | PRODUCT | BBS | CODE
Code | Memorial | Sample | Reference
List | -10 | -20 | -30 | -40 | -Latest
since 2000/10/01
#41 (2001/09/16 )
 GLUI日本語対応計画
#42 (2001/10/24 )
 MS-Wordでテクニカルイラストレーション
#43 (2001/11/14 )
 565マスク生成問題
#44 (2002/01/14 )
 順序なしリスト
#45 (2002/02/14 )
 キーリピート
#46 (2002/03/18 )
 How do 誘導?
#47 (2002/05/22 )
 初歩のテーリング
#48 (2003/03/18 )
 バーチャル大儲け計画

Tip Mark
欄外情報FOOTNOTE  参考リンクLINK  補足事項NOTICE
sanami@aya.or.jp

第41回GLUI日本語対応計画
はじめに

みなさん、こんばんわ。ここのところ DVD を収集しているのですが、まことにもっていただけません。洋画の DVD というと、字幕と音声がそれぞれ英語と日本語の二種類から選択できるのが普通です。当然のことですが、人によってどの組合せを選択するかが違うと思います。ちなみに、僕の場合は BGM(Back Ground Movie)状態で流すことが多く(垂れ流しってことですな)、必要以上に意識をもっていかれないように日本語音声・字幕なしがデフォルトです。

LD の場合では、音声も字幕も買う時点で固定されているので、プレイヤーに突っ込んで再生すればいいのですが、DVD ではそうはいきません。再生するたびに設定しなおさねばなりません。ところが、自動再生が開始すると最初に

この DVD に収録されているビデオプログラムは、一般家庭での私的視聴に用途を限定して販売されています。レンタル及び中古品売買取引等、その他の目的による利用は著作権者の許諾が必要になります。

この DVD に収録されている映像及び音声をその一部でも、著作権者の許諾なしに、複製、改変、上映、上演を行うこと及び放送、有線放送等により公衆に送信することは法律により固く禁止されています。

と表示されるのですが(20秒程度)、その間、スキップや音声・字幕設定、DVD メニューへの移動等の操作をすることはできません。せめて、この間に音声・字幕設定ができれば文句を言うのは控えるのですが、残念なことです。

などと思っていたら、「天と地」ではロゴの後に DVD メニューへ移動となっていました。

そんなこんなで不満を持ちつつも、LD に比較してサイズが小さいことと、値段がお手ごろ(2000円から)なことから俳優買いしていたりします。つい先日も JFK を買ってきたのですが、うかつなことに日本語音声が入っていないことに気がついていませんでした。で、どうなるかというと、たっぷり 206 分画面にくぎ付けになってしまいました(笑)。

というわけで、集中していないときの英語のヒアリングができないわけですが、そんな僕が今気になっているのは HONDA INTEGRA Type R の CM です。Imagine! チャチャチャチャ Integra Type R ・・・・と始まるのですが、その後に何を言っているかが覚えられません。何しろ、CM なので、気を許しているときにいきなり始まるわけで、録画しているわけでもないので、確認しようがないのです。ホンダのページ を確認しましたが、CM の動画は用意されていないのですね。もったいない。

GLUI日本語対応計画

そんな僕ですから、英語しか使えない環境でソースコードなどに適当な英語を記述しておくと、数日後には「ありゃ、なんだったけっけ?」となることが多々あります。とはいえ、ローマ字で書くのは恥ずかしいのでなんとか日本語が使えるようにと場当たり的に頑張るのでした。

さて、OpenGL をより簡便に利用する ToolKit として GLUT があります。GLUT ではメニューシステムとしてポップアップメニューが提供されますがユーザインタフェースとしては不十分です。GLUT 上でも Windows のテキストボックスやボタンなどの GUI 部品を利用できるようなインタフェースとして GLUI があります(図1)。この GLUI は GLUT 上のプログラムとして記述されているため、基本的にどのプラットホームでも利用できる点が非常に優秀です。しかし、残念なことに GLUI 上で表示される文字列には日本語が使えません。これを解決しようというのが今回の趣旨です。

GLUI の提供する GUI 例
図1:GLUI の提供する GUI 例

GLUI 上で日本語が表示できない原因は、ウインドウタイトルやポップアップメニューのように Windows が文字を描画するのではなく、GLUT の glutBitmapCharacter という関数でラスターフォントを自前で描画しているためです。

GLUT は DLL として提供されているため、理想に準じて修正を加えるといちいち配布しなければならなくなり、面倒です。プログラムの修正にしても GLUT::glutBitmapCharacter 関数では int 型で一文字ずつ描画する仕様なのに対し、GLUI では 1byte ずつ描画関数へ送り込むようになっているため、GLUT を日本語対応するだけでなく、GLUI も対応させる必要があるわけです。

どちらにしろ GLUI に変更は必要になるので、GLUT の DLL に変更しないで済む方向を考えてみます。GLUI だけを日本語対応する場合、GLUT::glutBitmapCharacter 関数は日本語対応になりません。しかし、日本語の場合には 1byte ずつ描画というわけにはいかなく、1 文字ずつ 1byte か 2byte かを判定しながら描画する必要があり、一文字ずつ描画というよりは文字列描画の関数のほうが有用性が高いと思われます。

と、適当に言い訳をかまして、GLUI だけの日本語対応を試みます。ちなみに、ライブラリ外部へのインタフェース(ひらたく言えば、glui.h)を変更しなければ、ヘッダファイルに手をつける必要もないのがポイントです。リンクするライブラリを変更するだけで日本語対応が可能ということです。

日本語フォントの準備

とりあえず、ラスタフォントのデファクトスタンダードの形式である FONTX 形式を入力とします。フォントは・・・・なんでもいいんですが、ここでは恵梨沙フォントを使用することにします。恵梨沙フォントの 8x8 は FONTX 形式の配布がなされていませんので、ふぉんと昆布を使って変換することにしましょう。

c:>fontconv elisa100.fnt


統一感を持たせるために、4x8 の半角フォントも用意します。こちらは FONTX 形式での配布なので変換なしでOKです。(伊藤栄一郎さんのページの DATA にあります)

さて、フォントが用意できたところで、次にすることは任意の文字に対応するフォントを取り出すプログラムの作成です。FONTX 形式のファイルフォーマットについては FONTX Home Page に解説があります。半角(1byte)フォントと全角(2byte)フォントによって少しデータフォーマットが違います(図2,3)。半角フォントは必ず 256 文字すべてが必要ですが、全角フォントではすべての文字を用意する必要はなく、文字のある範囲をテーブルに書いてあります。詳細は FONTX Home Page で確認できます。

FONTX 1byte 形式
図2:FONTX 形式の 1byte フォント場合

FONTX 2byte 形式
図3:FONTX 形式の 2byte フォントの場合

ということで、1byte フォントの場合、キャラナンバー num のパターンデータは次のようにゲットできます。

unsigned char *ptr = &bytes[sizeof(Header) +sizeof(Char) * num];


次に、2byte フォントの場合、キャラナンバー num のパターンデータは多少手間がかかります。

unsigned char *ptr;
int index = 0;

for(int i = 0; i < Tnum; i ++)
{
if(num <= CodeTable[i].end) break;
index += CodeTable[i].end - CodeTable[i].start + 1;
}
index += num - CodeTable[i].start;
ptr = &bytes[sizeof(Header) +sizeof(CodeTable) +sizeof(Char) * index];


GLUI の修正

FONTX 形式の Char パターンデータはビットデータとして保持されていますが、これは glBitmap でそのまま使えます。ただ一点、スキャンラインの順番が逆なことを除いては。(それは使えないってことでわ?)

さて、修正を加えるのは glui.cpp からです。ラベルで日本語を表示できるようにするだけなら修正はそんなに難しいことではありません。ラベルの描画に関係するのは、たった二つの関数だけです。

int _glutBitmapWidthString( void *font, char *s );
void _glutBitmapString( void *font, char *s );


見てのとおり、固定フォントなら簡単ですね。_glutBitmapWidthString は 1byte ずつ見ていき、幅を加えていけばいいだけのことです。レンダリングも簡単です。glut のソースから glutBitmapCharacter 関数部をごそっとコピペです。

そんなこんなで修正を加えた結果、無事に日本語を表示できるようになりました(図4)。

日本語をふんだんに使用した例
図4:日本語をふんだんに使用した例

GLUI のさらなる修正

動作をいろいろ試していると・・・・ありゃ?テキストボックスで日本語が使えません。う〜む。なぜだ。実際問題としてテキストボックスで日本語を使うような要求はほとんどないのですが、ここまできたら修正してしまいましょう。修正するのは glui_edittext.cpp です。GLUI_EditText クラスでは、文字の描画は直接 glutBitmapCharacter を呼び出しているため glui.cpp の修正が有効にならなかったのです。

修正を始めてから気が付いたのですが、GLUI_EditText の修正はかなり大事です。とにかく、何をするにも 2byte 文字を考慮していないので。それでも一つ一つやるしかありません。結局、ほとんどのメソッドに手を入れることになりました。で、その実行例(図5)。

日本語に対応した EditText
図5:日本語に対応した EditText

フォントの変更

さっそく後輩に試用させた(笑)ところ、「小さくて見にくい」と即答。そりゃ、液晶ディスプレイで 7pt 常用の人に合わせればそうなるわな。そんなことを言われたので、12pt の文字を探したのですが、FONTX 形式で 12pt のフォントは全然見当たらないのです。転がっているフォントの多くは bdf 形式です。bdf 形式は Linux の標準の形式であるために広まっているようです。

結局、データコンバートぐらい、いくらでもしてやるわい、と開き直り使いたくなるようなフォントを探すのでした。で、東雲フォントファミリーのまる文字(サンプル)に決定。配布は bdf 形式。ということで、GLUI を日本語対応していたはずなのに、いつのまにやらフォント形式の解説になりそうな気配が漂い始めるのでした。まったく、いきあたりばったりだなぁ。

bdf 形式については、itouhさんのページの BDF研究(その1) で詳説されています。できれば origin point も対応したいところですが、FONTX 形式が対応していないので中止。glBitmap 自体は(Boundary Box も含めて)対応できるので、どうしてもという方は glui.cpp の内部形式を変更してはいかがでしょうか。データ構造の勉強になると思います。

原点を考慮してパターンを移動する、といったことがなければ bdf 形式から FONTX 形式への変換は難しくありません。ビットパターンが 16 進テキストとして記述されているので、これをバイナリに変換するのが主な作業です。bdf 形式ではパターンに対応するコードが JIS 表記ですが、FONTX 形式では SJIS になります。ということで、JIS から SJIS にコード変換することが必要です。検索したらプログラムが見つかったので、そのまま流用します(こちら)。あとは、連続している領域を検出し、FONTX 形式の CodeTable を生成します。作成した 2byte フォントの bdf → FONTX 変換プログラムはこちら41-bdf2fnt.cpp (3897 bytes)。サポートはしません。

無事に変換ができたので、実行してみた結果。(図6)かな文字だけしか違わないので微妙なまる文字調ですが、満足満足。

まる文字をふんだんに使用した例
図6:まる文字をふんだんに使用した例

再度、後輩に感想を聞く。

後輩:まる文字やだ
さ〜:いーじゃんかよー、日本語使えるんだから
後輩:だって、デモのときに恥ずかしいじゃないですか
さ〜:うむ、そりゃそうだ
後輩:あと、英文字も直してくださいよ
さ〜:あー、わかったわかった

1 byte 文字の復帰

日本語フォントに関しては GLUT に用意されていなかったので、フォントを用意してレンダリングするのは回避できませんが、1 byte 文字まで FONTX 形式のフォントをレンダリングするのは失敗でした。固定ピッチになってしまったため、文字の大きさを 12pt 程度まで引き上げると以前よりも GUI ウインドウの幅が広くなってしまうのです。ということで、1 byte 文字をレンダリングする場合はもとのルーチンを使用することに変更しました(図7)。

1byte 文字は GLUT を利用する EditText
図7:1byte 文字は GLUT を利用する EditText

まとめ

今回日本語対応させたのは GLUI version1 ですが、GLUI には version2(以下、GLUI2) がベータ版ながら配布されています。GLUI2 は UI を GLUT のウインドウ内部に配置できるため(図8)、使い勝手がよさそうですが、GLUT 上でがんばって記述されているため微妙なところで動作があやしくなっています。僕が原因なのか、GLUI2 自体のミスなのかを究明中なので、とりあえず GLUI version1 を日本語対応してみました。ソースコードも添付しておきますので、興味・要求のある方は GLUI2 を日本語対応してみてはいかがでしょうか。

2001/09/19
気が向いたので GLUI2 も日本語対応してみました。後輩がうるさかったとかいうのが理由かどうかは黙して語らず。
GLUI version 1 日本語対応版41-gluij.lzh (185011 bytes)
GLUI version 2 日本語対応版41-glui2j.lzh (246727 bytes)

GLUI version2 beta 実行例
図8:GLUI version2 beta 実行例

そういえば、DVD ですが、PS2 はひどいですね。かなりの確率で再生中の不具合が発生するソフトがあります。不具合としては、次のようなものがあります。

ちなみに、すべて新品キズなしです。念のため。というわけで、パソコンで DVD を再生できる環境にするかなーと画策中です。

と思ったら、DVD プレイヤー買ったほうが安いとわ。うかつ。
それでわ。

Virtual Payment System100えん10えん1えんヘルプ

第42回MS-Wordでテクニカルイラストレーション
はじめに

みなさん、ポポペ〜。PocketPostPet が 1980 円なんですよ。んで、Windows CE マシンとして使えるんです。さらに、中古のマルチメディアカード(以下、MMC)が 1000 円/枚ポポペから書き込むとデータが破壊されますが1] なので、これを合わせればたった 2980 円でカラー液晶のシグマリオン級の速度をもった Windows CE マシンがあなたの手元に!この他にパソコンで MMC に書き込める環境が必要です2]
 1ポポペから書き込むと MMC 上のファイルシステムが破壊されます。PostPet を実行すると自動的に MMC 上にフォルダを作成しますので、Windows CE 化したポポペにおける読み込み専用カードとしてしか利用できません。
 2パソコンで MMC に書き込める環境が必要です。


そもそもの理由は、電車の中で立っているときでもテキスト打ちをしたいな〜と思ったことです。とわいえ、座っているときなら B5 ノートが使えるので、立っている限られた時間のために大金を費やす気にはなりません。そんなとき、思いついたのが E メール専用端末です。ものにもよりますが、多いのは携帯・PHS に接続して通信するタイプです。これだと 1 万円そこそこで買えます。ここで問題になるのは、僕の想定する利用法が通常の使用法とは異なる点です。僕が欲しいのはテキスト打ちのできるマシンであって、E メールを出したいわけではないのです。したがって、パソコンとデータのやり取りが可能である仕組みが必要です。

そんな前提で機種の選定を行っていたところ、やまpくんから PocketPostPet で Windows CE が動くとのアドバイスをもらい、検討した結果、さっそく買ってきました。MMC と MMC アダプタ含めて 1 万円から少し足が出る程度です本体 \1980 + MMC \1000 + カードリーダTDK-UA5 \3980 = \6960 ぐらいが最安?3]。 ということで、ポポペを弄くり倒す日々を過ごしているかというと、まったくそんなことはなくて、エディタを使える程度にしただけで、壁紙も変えてもいません。忙しいので。どのくらいかと言うと、朝 10 時から 1 時間のプレゼンをするのに、前日の午後 8 時にようやく時間ができるくらいです。ってゆーか、寝る時間が無いヨ!こんなことなら、シスプリ見てないで仮眠とっときゃよかった、です。100 歩譲ってシスプリは最終回だからしょうがないとしても、Game Wave はやめときゃよかった、です。それにしても、女キャラはすぐにクビになるのに奴らはなぜ毎週出てるのだろうとか思ったり思わなかったり。
 3本体 \1980 + MMC \1000 + カードリーダTDK-UA5 \3980 = \6960 ぐらいが最安?


テクニカルイラストレーション

さて、時間がねぇ!というときに作ったプレゼンは字だらけで聞いている人を夢の世界へと誘う魅力がたっぷりですが、万が一起きていられると「わっかんねぇよ!」と集中砲火をあびるというリスクもあります。僕のように気が弱い方はやはり正攻法でわかりやすいプレゼンを作るべきです。わかりやすいプレゼンのコツは図を多用することです。図と文、どちらでもOKなら迷わず図を取りましょう。しかし、図を描くには時間がかかるので、急いでるときには適当な抽象画になってしまいますが、それでは逆効果です。そんな忙しいあなたにお勧めするのが、テクニカルイラストレーション( TI )です。

TI は、マニュアルによく使われるもので、図1 のようなイラストがそうで、いろいろなところで使われているので、目にしたこともあると思います。


図1:TI の例(機械工学便覧より引用日本機械学会発行4]
 4日本機械学会:機械工学便覧 C2 交通,日本機械学会,ISBN4-88898-044-6


TI は、その図が正確であるという特長を持っているため、別個に描かれた部品を組み立てることもできたりと、マニアごころをくすぐります。三次元 CG に比較して安価に作成することができるので、なくなることはないでしょう。TI は、その正確性という性質上、描くためのアプリケーションを選びます。広く使われているのは Illustrator や二次元 CAD などのようです。僕も JW-CAD を使っており、CAD でなら TI も比較的容易に描画できます。

Illustrator や二次元 CAD を用いての TI 作成については、プロの方も解説されていたりするテクニカルイラストレーションの!!!5] ので、僕の出番はありません。やはり、このページは他の人がやらなく、かつ実用的なことを提案してこそ、読者の期待に沿えるのです(そういう方針だっけか?)。ということで、Microsoft Word を使います。実のところ、Word を使わざるを得ない状況になったんですけど。
 5田山達也さん:テクニカルイラストレーションの!!!
http://www.sky.sannet.ne.jp/t_tayama/


Word はバージョン間の差異が激しいことで有名です。ここでは、ある理由から Word98 を使うことにします。僕は Word95 の方がレイアウト枠が使いやすいので好きでしたが、新しいファイルを読めないのでは、ねぇ。Word97 を選択しないことはいまさら詳説するまでもありません。バグ多すぎ(笑)。OfficeXP(WordXP が入っているのか?) を使わない理由はアクティベーションが面倒だからです(ってゆーか、買ってないけど)。問題は Word2000 にしない理由です。Word 図のグリッドは 0.05pt きざみに設定できるのですが、Word2000 ではデフォルトの単位が「字」なんです。字って!

試してみましょう。僕のマシンは Word98 で統一してあるので、Word2000 の例は人のマシンで実行です(笑)。まずは、Word98 の場合です。グリッドの間隔を 0.5pt に設定してから、ダイアログを開き直します(図2)。普通ですね。


図2:Word98 でグリッド間隔 0.5pt を指定した場合

それでは Word2000 の場合です。同じようにグリッド間隔を 0.5pt と入力して、ダイアログを開き直します(図3)。


図3:Word2000 でグリッド間隔 0.5pt を指定した場合

「字」という単位付記(2003/04/03)21] が何を意味しているか(そもそも絶対的なのか)を探求してみるのも、それはそれで興味深いような気はしますが、ここでは触れないことにします。このあたりの訳のわからなさはさすが Microsoft っつー気がしなくもありません。たとえるなら、「銭湯帰りに上機嫌で鼻歌歌ってたらダークに紛れて鼻の穴にダイブしてくる蚊柱のよう!」朝霧の巫女 (c)宇河弘樹/少年画報社6] それこそ、「おまえなんか今すぐどうにかなっておしまい 世界美化の為に」って感じです(わからない人は単行本をご購入ください)。
 6朝霧の巫女1 朝霧の巫女 (c)宇河弘樹/少年画報社
YOUNG KING アワーズで連載中の巫女マンガ。倉子さんのロードスターの色が知りたいのは僕だけですか。
 21付記(2003/04/03)
ツール→オプション→全般で変更できます。


Word 利用法

そんなわけで、実はいかに使えないツールを工夫して使うか、というのが今回のお題だったりするわけです。などとこき下ろしたままでは、さすがにまずそうな気がするのでフォローしておきます。謎の誤差WMF を挿入する時や拡大時に表示がずれる7]が入らなければ Word の図作成ツールは便利に使えます。機能だけみれば他にもいいツールはありますが、印刷時のクオリティとファイルサイズ、インストールの手間、他人へのファイルの受け渡し等を考えると(残念なことに)better なアプリケーションです。あ、一太郎シリーズは使ったことないので比較の対象に入ってません(だめじゃん
 7WMF を挿入する時や拡大時に表示がずれる、300% の表示と 400% の表示が違うなどあげればきりが無く。


Word で図を描くにあたり、僕の日常的な使用法を紹介します。

図は挿入して使う
図を描く際にグリッドを駆使するため、別オブジェクトにしておかないと文章編集時に方眼紙状態になります。挿入オブジェクトとすることで図ごとにアイソメ図用のグリッドと正方グリッドの使い分けられます。二段組にぶち抜きの図を入れる場合にも便利です。[挿入]→[オブジェクト]→[Microsoft Word図]、で挿入できます。使用頻度が高いので数式オブジェクトとともにツールバー上にボタンを配置するよう、カスタマイズしておきましょう。

図には外枠をつける
挿入した図は、サイズを自由に決定することができます。上部および左側のルーラを使用することもできますが、僕は[図を閉じる]ボタンの横にある[図の罫線の解除]ボタンを使用しています名称と機能があっていない気がするのですけど。8]。 このボタンを押すと、オブジェクトのない余白部分を削除し、図より外にオブジェクトが存在する場合には図の領域を広げます。
 8僕には機能と名称が合っていない気がします。ちなみに、僕の中では図のリサイズボタンとして認識されています。


機能としては便利なのですが、困ったことに、このボタンを押すと今までグリッドにスナップして配置された点(端点等)がグリッドから微妙にずれます。この現象を回避するために、図に外枠の矩形を塗り潰しなし・線色透明で配置します。このようにするだけで、リサイズボタンを押してもグリッド上から移動しなくなります。

グリッドにはスナップが基本
図形描画では基本状態として、グリッドに対してスナップされるのが基本です。いくら Word で描く図でも、いや Word で描くからこそ、可能な場面では正確に描く必要があるのです。しかし、グリッド間隔 1pt、4 本おき程度ではちょっと複雑になるとグリッド交点以外に線を引く必要がでてきます。この場合、第一段階として 2 線分の交点の形で点を作成し、グリッドの設定でグリッドスナップを解除、描画オブジェクトに合わせるを有効にすれば交点にスナップできます。

文字はテキストボックス
図内に文字を入れる場合はテキストボックスを使います。位置を移動するなど使い勝手がいいです。テキストボックスとして使うときにボックス内の余白が無駄に多いと邪魔なので、塗り潰しなし、線色なし、テキストの周りの余白をすべて 0 にします。気づきにくいのはテキストを入力できる状態で[書式]→[段落]の「1ページの行数を指定時に文字を行グリッドに合わせる」を解除することです。これでテキスト上部の余白もなくすことができます。

右ドラッグを多用
図を描いていると、いちいち属性を変更するのが面倒になってきます。線を引くたびに太さ、線種、色…などとするのはいやですよね。そんなときに威力を発揮するのが右ドラッグです。コピーしたい対象を右ドラッグでずりずりと移動して、右ボタンを離すと、あら不思議。まるで AV-300 のようにコピーし放題です。ちなみに、その場にコピーしたい場合は右ボタンをホールドした状態でちょっと待ってから離すとコピーメニューが表示されます。知ってました?

図は左上近くに描く
図を編集中にオブジェクトを削除するときなどにテキスト入力位置が画面内におさまるように表示位置が自動的に移動することがあります。これをやられると、編集作業に大きく影響するので、移動を避けるために常にテキスト入力位置を画面内におさめた状態で図を作成するように心がける必要があります。

塗りつぶしは最終手段
ワイヤーフレームでの描画において隠れ線処理を行う場合、一番簡単なのは depth buffer を用いた隠れ面処理に基づく方法です。これを Word で実現するには、面分の稜線をたどった閉図形を作成することで解決できます。それぞれの面分に対応する閉図形を作成し、これを塗り潰した上で順番を適宜入れ替えることで depth buffer と同じ動作を得ることができます。塗り潰したオブジェクトを多数重ね合わせた場合、印刷時に不具合が発生しやすくなるため、画面表示の速度やオブジェクトの操作(順序を考慮するかどうか)を考え合わせると、塗り潰しよりは手動で隠れ線処理をしたほうがよいのではないかと思います。線画の場合は、ですが。

軸の準備

アイソメ図を作成するにあたり、まずグリッドを表示します(表示→グリッド線)。デフォルトでのグリッド線は行線だけなので図形描画ツールバーを表示(表示→ツールバー→図形描画)して、グリッド線の表示設定をします(図形の調整→グリッド)。このときのグリッド線の間隔が最も重要です。

アイソメ図では 1 軸を縦方向にとり、左右に 60 度ずつのところに 1 軸ずつとります。ここではそれぞれ Y-Z-X とすることにします(図4)。このとき、X,Z 軸上の ±30 度の角度を持つ直線をいかにして引くかということが問題になります。


図4:アイソメ図における 3 軸

グリッド線の縦横の間隔比が 1:1 のままで 30 度の直線を引くには 1:cos30 の比率を整数比に近づける問題とほとんど一緒です。方程式にすると面倒ですが、cos30(≒0.866)を整数っぽい数になるまで足せばいいだけのことです。で、足していくと 7 倍で整数に近くなります。

7 x cos30 = 6.062

ここで、上式は 30°の方向に 7 だけ進むと X 軸上では 6 進んでいる、ということを意味しているので、実際のグリッド上に 30°の線を引くために Y 軸上でどれだけ進むのかを知る必要があります。で、それは sin です。

7 x sin30 = 3.5

したがって、30 度の直線を引くには横に 6、縦に 3.5 の位置を通る傾きで引けばいいことがわかりました。しかし、実際に適用してみるとわかりますが、グリッド線の間隔に対して実際に使えるグリッド交点の数が非常に少なくなります。これを回避するには横方向は 0.1pt 間隔 12 本ごとと縦方向 0.1pt 間隔 7 本ごとでも可能ですが、見にくい上にスナップ可能な点が増えて使えません。ということで整数倍、たとえば 10 倍の 1.2pt x 0.7pt などが使えます。

ということは今気が付いたのであって、実はこんなことを考えずとも 30 度を得ることができます。sin30 = 0.5 なので縦方向を無視すると、横方向のグリッド間隔を cos30 の倍数にすることで 30 度が得られます。cos30 が理想ですが、Word98 では 0.05pt が最小単位になっているようなので、近似値 0.85pt が適当ですこちらの方が誤差が大きくなります。9] 。僕はこちらの方法なので、以降の説明もそうなっています。
 9どちらの方法も角度に誤差が発生します。1.2pt x 0.7pt の場合で +0.26 度、0.85pt x 1.0pt で +0.47 度です。
実用上は問題ありません。
長さについて厳密に扱うためには縮み率を適用する必要があり、この場合は 0.70pt x 0.80pt になり、誤差は -0.26 度です。


表示するグリッドですが、すべてのグリッドを表示すると画面が灰色になりかねないある程度以上に密度が高くなるとグリッドが表示されなくなるようです。10] ので何本かおきに表示します。もちろん好みによりますが、すべてのグリッド交点を簡単に認識可能な最低限の本数、4 本に設定するといいと思います。
 10ある程度以上に密度が高くなるとグリッドが表示されなくなるようです。


平面で構成される物体

さて、3 軸を決定できたことで立方体(直方体)を描く準備が整いました。さっそく描いてみます。まずは単位立方体です。各辺の長さが 1 となるようにするわけですが、基準がないので適当で構いません(それじゃただの立方体だよ…)。Y 軸を基準として 2 マス分の長さに線分を描きます。これが単位長となるわけです。次に X、Z 軸に単位長の線分を描きます。下準備を入念に行っておきましたので、簡単に描けます。2 マス分の長さを X、Z 軸上に描くには、横に 2 マス、縦に 1 マスです。あとは、すべての可視な稜線を描けば立方体が完成します(図5)。


図5:アイソメ図による立方体

立方体が描ければ直方体も簡単に描けます。長さに気をつければ他に問題はないはずです。さらに、傾きのある面分も描くことができます。ワイヤーフレームにおける面分は、その面分を囲む稜線が描ければいいだけの話なので。これだけでも実用的なレベルの図を描くことができるようになります(図6)。


図6:それなりに実用的レベルの例(引用元:テクニカルイラストレーションの!!!サンプルを書き直しました。11]
 11田山達也さん:テクニカルイラストレーションの!!!
http://www.sky.sannet.ne.jp/t_tayama/
Lesson 5 の 1 を書き直しました。



図7:それなりに実用的レベルのアイソメ図の例
円および円柱を含む物体

まず、Y 軸方向に伸びる円柱を描いてみます。とりあえずということで、寸法は無視して描いてみます。円柱の底面は 35 度円で表され、この比率は円を 35 度に傾けた状態ですアイソメ図の Y 軸は投影面より手前に約 35 度傾いています。12]から、横 1 に対して縦 sin35 になります。今、グリッドが 0.85pt x 1pt の比率だとすると、sin35(≒0.574)に近い比率を得るには横 2 グリッド(1.7pt)縦 1 グリッド(1pt)で 1pt / 1.7pt=0.588 が得られます。まぁ、だいたいこんなとこでしょう。試しに描いてみます(図8)。
 12アイソメ図の Y 軸は投影面より手前に約 35 度傾いています。スクリーン座標系(画面右を X 軸正、画面上を Y 軸正、画面奥を Z 軸正)からアイソメ図の座標系へ変換するには、Y 軸中心に 45 度回転後、Y 軸を手前に 45 度倒します。逆順でも同じですが。



図8:アイソメ図における円柱の例

次にそれぞれの軸方向に伸びる底面を単位円とする円柱です。単位立方体を描き、それぞれの面から生やすことにします。まずは完成図から(図9)。


図9:単位立方体と単位円柱を組み合わせた立体例

では、作成法です。まず、横 2 グリッド縦 1 グリッドの楕円を作成します(図10)。


図10:単位円柱の作成手順その1


このときの 35 度円の直径は 1.70pt となっていますので、これを 2pt にするには 2pt / 1.70pt ≒ 1.18 倍、投影時の円の呼び直径はさらに 1.22 倍する説明は割愛。13]ので、これをあわせて 2pt / 1.70pt x 1.22 ≒ 1.44 倍しますが、グリッド比が微妙に変化している関係で 1.43 倍になります。楕円上で右クリックし、[オートシェイプの書式設定]→[サイズ]で 143% に拡大します。これを 1pt x 1pt の正方形上にうまく移動するとちゃんと内接することがわかります(図11)CTRL キーを押しながらカーソルキーで移動で微調節できます。14]
 13なぜ 1.22 倍になるかの説明は割愛。説明し始めるとキリがありません。
 14CTRL +カーソルキーで図形移動の微調整ができます。



図11:単位矩形と内接する単位円

上底面と下底面を作成すれば、ふたつの接線を引くことで円柱の透視図が完成します。この透視図の作成法を用いれば隠れ線処理した図を得ることも簡単です。といっても、Word には線分の切断機能などというものはありませんので、手動で作成です。使うのは[オートシェイプ]→[基本図形]の中の円弧です。この図形は左上から右下にドラッグすることで第 1 象現の円弧が得られます(図12)。


図12:基本図形の円弧


あとは 143% に拡大、適宜反転を施すことによって半円弧が得られますからこれを組み合わせることで隠れ線処理した円柱が作成できます。黄色のハンドルを使うことで半円にできますが、意外に難易度が高いのでハンドルがグリッドにスナップしてくれないためです。15]素直に反転した方がよいでしょう。うまく調整できれば黄色のハンドルで白色のハンドルが見えなくなります(図13)。
 15ハンドルがグリッドにスナップしてくれないためです。



図13:正しい半円の例誤差でハンドルがずれます。16]
 16よく見ると、誤差が発生して横方向センターの白色ハンドルの位置がグリッド線からずれています。


Y 軸方向に伸びる円柱が描けたので、XZ 軸方向へ伸びる円柱を描きます。と言っても難しいことではありません。円柱をグループ化し、左右 120 度回転すればいいだけのことです(図14)。


図14:正しい半円の例

円柱面を含む物体

単独で存在する円柱に関してはそんなに難しくありませんでした。次は円柱面と平面が接している場合です(図15)。


図15:円柱面と平面が接する例

標準図形の円弧は黄色のハンドルを使うことで調整できることは示しましたが、同時に謎の誤差が常に発生するため、ズレを確認するための楕円を下に描いて円弧の調整を行います。描いてみればわかりますが、欲しいのは 30 度の線で分割される半楕円なので、楕円と直線を用意した上で、円弧を調整します(図16)黄色のハンドルは中心から遠くを移動することでより正確になります。17]
 17黄色のハンドルは中心から遠くを移動することでより正確に調整できます。



図16:円弧の調整

円弧の調整後、143% に拡大…するとなぜか大きすぎたりするので 140% 程度に。このあたりは柔軟に対処します。厳密に計算したところで、どうせずれます。あとは位置の微調整を繰り返して、隠れ線処理のためにコピーして楕円の調整をして…とすると完成です(図17)。


図17:円筒面と平面の接続する立体

円柱面を含む立体をうまく描くことができれば多くのものを描くことができたことに…なりませんが、簡略化すればたいていのものが描けます。自由曲面をどのように描くのかということになると、空間認識力や表現力が要求されますし、そんなものを説明できるとは思いませんので、このくらいで止めておきます。ってなことで逃げられたかな?

おまけ程度の色塗り

おまけ程度というのは、シェーディングっぽく塗る際に円柱面が Y 軸方向へ伸びている場合と制限されるためですWord のグラデ塗りが任意角度をサポートしていないためです。18]。平面で構成される立体ならわりと普通に塗れます。適当な位置に光源を想定し、影を考えずにフラットに塗ります。任意の図形を塗るのは[オートシェイプ]→[線]→[フリーフォーム]で塗る図形の稜線をなぞります。線を残すので、フリーフォームの図形は最背面へ順番を変更します。これを繰り返せば簡単に適当な塗りができます(図18)。流行のトゥーンシェードっぽいですな。
 18Word のグラデ塗りが任意角度をサポートしていないためです。



図18:シェーディング(っぽぃ)処理を施した例

輪郭線以外の稜線にハイライト効果を与え、必要ない稜線を削除すると、少し角が丸くなったように見せることができます(図19)。


図19:角を丸めてみた例
おわりに

プログラマと言えども、プログラムだけ書いていればいい時代は終わりました。これからのプログラマはプレゼンもこなさないといけません。僕なんか、今年はプログラムの行数よりもテキストの行数の方が多くなってます(それは関係ない)。TI を使ってちょっと立体っぽくするだけでも注目されます。お試しアレ。

そんなこんなを書き連ねていたらいつの間にやら一ヶ月立ってるヨ!。その間も確実にポポペ症候群は魔の手を伸ばしているようで、僕は数人に買わせただけなのに見回しただけでも 10 人以上、話を聞くと 20 人ぐらいは感染しているようです。ねずみ算状態です。というわけで、あなたもいかが?
それでわ。


参考文献
 日本機械学会発行4 日本機械学会:機械工学便覧 C2 交通,日本機械学会,ISBN4-88898-044-6
テクニカルイラストレーションのサンプルを載せるためにふととった一冊(なんちゅー説明や)。

 テクニカルイラストレーションの!!!5 田山達也さん:テクニカルイラストレーションの!!!
http://www.sky.sannet.ne.jp/t_tayama/
テクニカルイラストレーション作成技法について詳しく解説しておられるページ。ご本人はプロということで仕事募集中。ページの随所で紹介されるサンプルはすげぇの一言。こういう職人芸もあこがれますねぇ。

 朝霧の巫女 (c)宇河弘樹/少年画報社6 朝霧の巫女 (c)宇河弘樹/少年画報社
YOUNG KING アワーズで連載中の巫女マンガ。世界設定がどうとか言うよりも、その独特のノリと読ませる展開が面白い。これを言葉で表現するのは難しいので、とりあえず、「買って読んどけ!」
宇河弘樹さんのページ日瑠子陛下行幸のようす on web
宇河弘樹ファンページ あやかしの茶や

  遠藤俊次:テクニカルイラストレーション ●立体製図の描き方●(増補版),理工学社,ISBN4-8445-2453-4
テクニカルイラストレーションを始めるにあたり、お薦めの一冊。これだけあれば基本的な知識は十分です。伸び率の計算方法についても丁寧に説明してあり、三次元 CAD で等角投影するにも参考になります。価格もお求め安い \1,480(時代とともに値上がりしている)。

補箋(2001/11/10)

本文中の図は適当に描いたつもりだったのですが、意外に評判がよかったことと、近藤さんが日記で「かっこよくボルトを書いてみたい」と書かれていたので、調子にのって追記します。まずは、PowerPoint で作成したボルト(図19)。発表内容には突っ込まれずに、「そのボルトはどうやって描いたの?」と教授に突っ込まれたシロモノ。作り方はいたって簡単、六角形を立体化、楕円を回転・立体化で適当に並べただけ。よくよく見ると適当なのがよくわかります。これは TI というコトバを知る前の作品なので、等角投影は使ってません。


図19:ボルト

そして、こちらは今描いたコネクティングロッド、通称コンロッド(図20)。標準機械製図集理工学社19] に載っていたのでうりゃっと描いてみました。ちなみに、制作時間は下書き(図20右)に 1 時間、仕上げ(図20左)20 分でした。画面をキャプチャすると汚いので、印刷してスキャナで取り込みました(笑)。残念ながら Y 軸以外の円柱曲面があるので塗れませんでした。
 19大柳康,蓮見喜久著,北郷薫監修:JIS にもとづく標準機械製図集(第 3 版),理工学社,ISBN4-8445-2262-0



図20:連接棒

上のコンロッドを印刷した紙を後輩くんに見せたところ、「どうやって描いたんですかー?」と聞かれたので、実演。立方体の角を R 面取りするだけのものでしたがシェーディングまでの作業を見せたら非常に面白がってくれました。と、いうことで次はシェーディングまで行ったピストンです(図21)。作成時間は全工程で 1 時間かかってません。


図21:ピストン作成の工程(動画)

コンロッドとピストンの作成例ですが、おそらく WORD で描いたことが信じられないと思われますので(笑)、ファイルをダウンロードして確認してみてくださいファイルをダウンロード20]。作成効率ですが、CAD・DRAW 系アプリケーションと比較しても遜色ありません。画面・プリンタ出力あわせなのが逆に適当チックでいいようです(笑)。
 20WORD で描いたコンロッド・ピストン:42-conrod.lzh (42838 bytes) scaned with Norton AntiVirus


Virtual Payment System100えん10えん1えんヘルプ

第43回565マスク生成問題
はじめに

みなさん、うぃーっイヌっネコっジャンプ! (c)はっとりみつる/講談社1]。以前は挨拶のときは「うぃーっス」だったのがマンガの影響で変わったとは恥ずかしくて言い出せないけど誰も気にしていないので何も問題ない今日この頃です。といっても、「うぃーっス」は久川綾がらみだったりするので、それはそれでアレですが。
 1イヌっネコっジャンプ! イヌっネコっジャンプ! (c)はっとりみつる/講談社
Uppers で連載中の幅跳びマンガ。妙にえろチックな主人公の妄想とは対照的に状況は何も変わらずにキャラだけ増えていくと言うとダメなマンガのようですが、面白いです。メインの三角関係を構成するはずの和月さんの影が週を追うごとに薄くなっているのが気になります。っつーか、主人公はいつになったら陸上部に入るのだろう…


「イヌっネコっジャンプ!」は、最初の 2 単語が「っ」で韻を踏んでいるわけですが、3 つ目の単語はリズムを微妙に変えることでメリハリをつけています。似たような(似てるか?)例として「MSX」をあげてみます。最初の 2 文字では「トン・トン」と着て最後のXではそのリズムを変えています。「MSX2」では最後の 2 が発音上邪魔なのは言うまでもなく、「MSX2+」「MSX TurboR」なんてのはもってのほかです。とはいえ、「MSX」なら発音しやすいかというとそうではなく、すべての文字が「エ」で始まったり、「エックス」が言いにくかったりでダメダメです。せめて「エムエスゼクス」ならよかったのに。

さて、最近はパソコンの性能があがったとは言え、メモリのアクセス速度が相対的に低下しており、Full/Hi-color 化の障害となっています。遅いメモリで多彩なエフェクトを行うには省メモリ、つまりは低解像度・低色解像度の方向にならざるを得ず、近年の低スペック動作を前提としたゲームは 640x480 で 256 色ということになっています。ここで思い出して欲しいのが 256 色表示なら MSX2 ですでに実現されていたということです。ってことはですよ、もし MSX2 を冷凍庫に突っ込んでオーバークロックすれば今でも現役でいられるかも(笑)

残念なことに MSX2 の 256 色モードはパレットが固定だったため、より高解像度の 512x212 で 16 色パレットのタイリングのほうが使われていた記憶があります。なぜ 256 色モードが固定パレットなのかは知りませんが、とにかく 256 色= 8bit を RGB ごと 3 つのビットフィールドに分割していました。

8 ビットを 3 つのビットフィールドに分割するのですから、どれかが割りを食うことになります。B を 2 ビットにしたのは人間の視覚特性その1、色彩よりも明度に敏感であることを拠り所にしています。B は明度に対する影響力が弱いため、B の解像度が低くても問題ない、という論理です。だからといって 4 階調で問題ないかというとそんなことはないようにも思いますが。

呪いの 565 マスク

こんな話は過去のことと思いきや、16bit color になっても同じ問題が発生します。これが 555/565 問題です(今、そう呼ぶことにした)。16 は 3 で割り切れないので、余りの 1bit をどうするかでビットフィールド構成が変わるわけです。555 形式では、余った 1bit をα値(or マスクビット)に、565 形式では明度に影響する度合いが大きい G のフィールドに割り当てています。

基本的にどちらの形式にも一長一短があり、優劣を判断することはできないわけですが、ソフトウェアでピクセル処理する場合には 565 形式が劣ります。一般的な演算に関しては優劣はないのですが、飽和加算・飽和減算などのような演算では(現在提案されているアルゴリズムにおいて)各ビットフィールドの最上位のビットからマスクを生成すること(図1)が必須です。


図1:各ビットフィールドごとのマスク生成

555 形式に対するマスク生成で(知りうる限り)最短は以前提案した手法で 3 ステップです。C言語で記述すると次のようになります。

mask = ((RGB >> 4) + 0x3def) ^ 0x3def;
この手法の核となるのは、1bit のデータを複数のビットマスクに伸長する部分です。

次のように表記法を定めます。

A = { 0, 1 }
a = ~A
1 ビットのデータは A と表現できます。a は A の補数となり A を反転した数です。このとき、1bit のデータを複数のビットマスク、例えば 5bit に伸長する部分は次のように記述されます(図2)。
mask = (A + 1111) xor 1111


図2:1 ビットデータを 4 ビットマスクへの伸長

ここで、

A + 1111 = Aaaaa
については後で触れることにして、話を進めます。555 形式に対するマスク生成アルゴリズムにおいて、各ステップごとの状態は次のように示されます(図3)。


図3:555 マスク生成アルゴリズム

このアルゴリズムを 565 形式に対して適用した場合、G のフィールドの最下位ビットが常に 0 となってしまうこと(図4)が問題の発端です。


図4:565 形式に 555 形式のアルゴリズムを適用した場合
565 形式用マスク生成アルゴリズムの考案

555 形式のアルゴリズムを 565 形式用に修正します。まずは叩き台から。第 23 回暁のコーダ部屋,第 23 回:PSET for WonderWitch2] で触れている任意のビットを左シフトする手法を用いることでマスク生成が可能です。C言語による表記は次のようになります(図5)。

mask = (((((RGB >> 4) + 0x0801) & 0x1042) >> 1) + 0x7bef) ^ 0x7bef;


図5:6 ステップでの 565 形式のマスク生成アルゴリズム

 2暁のコーダ部屋,第 23 回:PSET for WonderWitch
http://www.aya.or.jp/~sanami/peace/memorial/code21-30.html#CODE23


このアルゴリズムは見てのとおり 6 ステップを必要とします。

より高速な 565 マスク生成

一般のアルゴリズムとして考える場合、アセンブラレベルでのキャリーフラグ等はなるべく使用しないほうがよいことは言うまでもありません。もちろん、言語・処理系によってどこまでの処理が可能であるかは異なりますので、言語レベルとしてはC言語を標準とすることにします。この条件において 5 ステップで 565 形式のマスク生成のアルゴリズムを提案します。

このアルゴリズムはコンピュータ内の数値表現を正しく理解しているなら誰でも見つけることができます。もちろん、ノーヒントで、というのはハードルが高いのは言うまでもありませんが、ある程度のヒントがあれば、あとは 5 命令の組合せなので論理的思考力の問題になると思われます。試しに、近くにいた後輩くん数人にヒント付き(夕飯おごり付き)で解かせることにしました。結局、きちんと解答を得られるまで考えてくれたのは 4 人中 1 人だったので、継続力・忍耐力・食欲あたりが必要かもしれません(笑)。

ともあれ、頭の体操によろしいと思いますので、後輩くんに与えたヒントで解けるか試してみてはいかがでしょうか。ヒントは、ここまでに述べた基本アルゴリズムおよび算術シフトの使用、の 2 つです。

算術シフト命令

算術シフトって何?な人のためにちょっち解説します厳密にはC言語に算術・論理の区別はありません3]。 シフト命令には、一般に論理左シフト・論理右シフト・算術左シフト・算術右シフトの 4 種類があります。ただし、論理左シフトと算術左シフトは同じ動作をするため、3 種類ということもできます。
 3厳密にはC言語に算術・論理の区別はありません。
負数の右シフトの結果は不定になります…が、一般に変数の型が符号なしでは論理シフト、符号付きなら算術シフトの使い分けがなされます。ですが、もしあなたの回りに不定だと自慢気に言われる方がおられるなら、黙って乗算・除算で代用しましょう。コンパイラの最適化はそのような方よりもよっぽど優秀なので、シフト命令に置き換えてくれます。


左シフトはビットパターンを左(上位)に移動します。シフトすることによって空いたビットには 0 が入ります。左へ n ビットシフトすることは 2 の n 乗の数を掛け合わせることと等価です。左 1 ビットシフトの動作およびその例を図 6 に示します。


図6:論理左 1 ビットシフトの動作およびその例

論理右シフトはビットパターンを右(下位)に移動します。シフトすることによって空いたビットには 0 が入ります。したがって、右へ n ビットシフトすることは 2 の n 乗の数で除算することと等価です。論理右 1 ビットシフトの動作およびその例を図 7 に示します。


図7:論理右 1 ビットシフトの動作およびその例

算術右シフトは基本的に論理右シフトと同じです。違うのは、シフトすることによって空いたビットは、もともとの最上位ビットで埋められるところです。算術右 1 ビットシフトの動作およびその例を図 8 に示します。


図8:算術右 1 ビットシフトの動作およびその例

さて、すべてのヒントを示しましたので、考えてみたい人は考えてみてください。僕の方は閑話休題としゃれこむことにします。

閑話休題

お題は A + 1111 = Aaaaa についてです。この等式は 1 ビットの値 B が n 個並んでいるパターンを Bn 、「.」をビット列の連結記号(0.1 ならビットパターン 01を表す)とすれば、次のように一般化できます。

A + 1n = A.an
この等式が常に成立することを証明するには数学的帰納法を用いれば簡単です。数学的帰納法は、二つのステップによって証明します。
  1. n = 0 のときに成立することを証明する
  2. n > 0 において、n-1 のときに成立することを仮定して n のときに成立することを証明する
この二つのステップを証明することで、すべての n は自動的に証明されるわけです。

1. n = 0

n = 0 の場合には左辺の 1n は 0 になり、右辺の an は空ビット列になります。したがって、

A + 0 = A
となり、正しい。

2. n > 0

A+1 の最下位ビットについて考えます。A=0 のとき、A+1 の最下位ビットは 1 であり、a となります。A=1 のとき、A+1 の最下位ビットは 0 であり、a となります。したがって、A+1 の最下位ビットは a と書けます2001/11/24 修正4]
 42001/11/24 修正
足し算の結果を間違えてました。NOHE さん、ありがとうございます。


次に繰り上がりビットを考えます。A=0 のときは、A+1 の繰り上がりビットは 0 なので、A と書けます。A=1 のときは、A+1 の繰り上がりビットは 1 であり、A と書けます。したがって、A+1 の繰り上がりは常に A と書けます。

以上から、

A + 1n = (A + 1n-1).a
の等式が成り立ちます。n-1 では成立すると仮定すれば、
A + 1n = (A + 1n-1).a = A.an-1.a = A.an
となり、最初の式が成立するため、正しい。

したがって、最初の式

A + 1n = A.an
が証明されます。

答え合わせ

さて、夕飯をだしに後輩くんたちに解かせてみたのですが、説明の上では僕の考えた命令列よりもよい解答が得られたのでした(本質的には変わらないが、見た目が違う)。これは僕も予想してませんでして、夕飯をおごる甲斐があるってものです。ということで、Duo くんの解答で解説することにします。

Duo くんの解答は、「>>>」が算術シフトを表すとして、次のようになります。そのステップを図 9 に示します。

mask = ((((rgb + 0x0010) >>> 5) & 0x0821) + 0x7bef) ^ 0x7bef;


図9:5 ステップの 565 形式のマスク生成アルゴリズム

…えーっと。解説のしようがねぇ!ちなみに、僕の考えたアルゴリズムは、R のマスク部が微妙に違うだけで、次のようになっていました。

mask = ((((rgb + 0x0010) >>> 5) & 0xf821) + 0x03ef) ^ 0x03ef;

おわりに

このアルゴリズムは DWORD/QWORD へも適用が可能で、C言語レベルでの記述も可能です。DWORD/QWORD ではアンダーフローした b が下位の R フィールドに食い込むため、加算および排他的論理和の値に注意が必要ですが、難しい問題ではありません。C言語レベルでの記述は、rgb が符号無しで扱われることが多いため、キャストしてシフトすればOKです。具体的には次のようになります。

(unsigned short)(((signed short)(rgb)) >> 5)
…がまんしてください。

ところで、MSX2 を冷凍庫に入れる話ですが、よくよく考えてみるとメインメモリが全然足りないので DIMM あたりを増設できるようにして、PCM の再生とかが可能なようにサウンドも強化して、当然プログラムサイズが大きくなるので HDD、CD-ROM をつけて、いまどき 4MHz もないので 2GHz ぐらいにしてしまえば 2D ゲームマシンとしては最高ではないでしょうか。

…それって、エミュレータ?
それでわ。


参考文献
 イヌっネコっジャンプ! (c)はっとりみつる/講談社1 イヌっネコっジャンプ! (c)はっとりみつる/講談社
Uppers で連載中の幅跳びマンガ。もしくは人間人工衛星マンガ。僕は3巻158Pの和月さんが気になって気になってしょうがありません。購入して確認だ!
はっとりみつるの落書きのページはっとりみつるの落書き(Uppersのページからマンガ&エッセイ経由

 暁のコーダ部屋,第 23 回:PSET for WonderWitch2 暁のコーダ部屋,第 23 回:PSET for WonderWitch
http://www.aya.or.jp/~sanami/peace/memorial/code21-30.html#CODE23
手前みそですいませんが、任意のビットを左シフトする手法のページ

補箋(2001/11/23)

サニーの性能に不満はなくともコーナリング中にロールが大きくて上体が左右に振れて腹筋が痛い今日この頃、アセンブラ限定での別解を思いついたので先を越されないように報告しておきます。

このアルゴリズムのキモとなるのは、adc(ADd Carry)命令です。この命令、レジスタ幅より大きいサイズでの加算を行うことを想定して実装されました。加算する際に、キャリーフラグが立っていれば 1 を加えます。この命令を工夫して使うと、レジスタ幅を 1bit 広げることができます。

adc 命令によって微調整する以外はすでに提案済みのアルゴリズムと同等です。eax レジスタに入力フラグが格納されているとして、アルゴリズムは次のようになります(図11)。

shr eax, 5
adc eax, 0x0400
and eax, 0x0821
add eax, 0x7bef
xor eax, 0x7bef


図11:565 マスク生成アルゴリズムの別解

ここで注目すべきは、adc 命令によってキャリーに存在した B のフラグ情報が最下位ビットに移動できることです。難しいことは何もなく、キャリーフラグが立っているときだけ 1 が加えられため、そのように表記できるだけのことです。

以上ですべてなのですが、キャリーを使う手法は利用するには制限が多すぎます。まず、どの環境でも問題となるのは、シフト命令と adc 命令の間にキャリーフラグを変更するような命令を挟めないことです。さらに(Pentium シリーズですが)ペアリング規則上、好ましくありません。シフト命令と adc 命令の両者は u パイプでの実行しか許されていません。

なんにしろ、C言語レベルでの表記が可能となっている今、わざわざ制限の多くなる手法を選択する利点はないと言えましょう(自己否定中)。そんなわけでバケットシートの購入を目論んでいる日々なのでした。

それでわ、レッツドライブゴーゴー!

Virtual Payment System100えん10えん1えんヘルプ

第44回順序なしリスト
はじめに

みなさん、おぃーっス。年末年始も僕にとっては銀行の開いていない連休と同じで、目新しいことと言えば、風邪をひいて寝込んでいたぐらいしかありません。

さて、新年早々、圧縮技術について興味を惹かれる発表がなされています。一言で言えば、「ランダムデータに対して圧縮が可能」ということで、「100分の1以下を実現する」との主張です元記事1]
 1ZDNN: 100 分の 1 以下を実現するデータ圧縮の新技術
http://www.zdnet.co.jp/news/reuters/020110/e_researchers.html


これに対して「眉唾もの」との評価が多いようですが、僕は実現可能性は高いと思っています。その根拠について圧縮方法から簡単に説明することにします。っつーか、そんなに詳しくないので簡単にしか説明できませんけどね!

圧縮、ここでは可逆圧縮(lossless)を実現する場合、一般的に二つの手法が核となっています。

  1. 符号化
  2. 辞書

符号化を用いた圧縮

出現頻度の高いデータを短いデータ列で表現することで全体としてのデータ量を少なくします。符号化技術は各種特許がとられているので、使用には注意が必要なようです。今回の話には深く関係しないので、詳細は割愛します。

辞書を用いた圧縮

データ列が辞書のどこにあるかという情報で表現することで、データ量を少なくします。例えば、sample という単語を辞書で圧縮するなら、p379、l82 のようにするわけです。この辞書を静的とする方法や、動的に更新する方法があります。ランレングスも一種の辞書として考えてもいいと思います(僕の勝手な分類です)。

乱数列の圧縮

ファイル圧縮を試してみればわかりますが、どんなに圧縮率がよい圧縮法で圧縮を繰り返しても、ある程度よりは小さくできなくなります。このラインがファイルの持つ本質的な情報量と言われます。この状態では、すべてのビット列の並びの出現頻度はほぼ等しくなるために符号化での圧縮ができませんし、ビット列の繰り返しや規則性もありませんので辞書を用いた圧縮もできません。このビット列は乱数列と見なすことができ、乱数列に対して圧縮ができない、と言うことができます。

ここで辞書として数列を選択します。もし、圧縮対象となるデータ列と同じデータ列を生成する数列を知ることができるなら、乱数列でも圧縮することができます。データ列全体が無理にしても、それなりの長さのデータ列を自由に生成することができれば圧縮が可能となります。

数列を辞書として使う利点は、少ないデータからデータ列を得ることができる点です。例えば、数列を生成する漸化式が次の形で表せるとします。

xn+1 = F(xn)
この場合、任意のデータ列は xn といくつのデータを得るかの二つの情報で表すことができるようになります。

乱数列を辞書とする方法によって、それなりに乱数列の圧縮が可能であるとします。圧縮後のデータ列が乱数列であれば同様の手法を、乱数列でなければ従来の圧縮を行うことによって再圧縮が可能となります。これを繰り返せば最終的には元データの 100 分の 1 どころかもっと短くすることも可能なはずです。

したがって乱数列の圧縮は、任意の乱数列を生成する方法を発見することに他ならず、そのことは

ZeoSyncは数学用語を使って,自社の技術を以下のように説明している。「自然発生パターンを意図的に作り出して,エントロピーライクなランダムシーケンスを形成するもの」
という説明からも理解できると思います。数列を生成する漸化式を複数用意し、圧縮もとのデータに演算を施す(png のように前のデータと add/sub/xor 等の演算をしておく)ことで任意のデータ列とマッチする乱数列を見つけやすくするなどの方法も考えられます。

従来の圧縮手法の共通の問題点は、複数回の圧縮(高次圧縮)が有効でないことにあります。一回の圧縮率が高くなくても複数回の圧縮が保証されるのであれば、用途が限定される(動的圧縮には適さない)とはいえ、有効には違いありません。以上の理由から可能性が高いと判断するわけです。

メモリフラグメンテーション

このように、一定に順序付けられたデータはそれだけで有用です。乱数を数列によって得るようにすれば、すべてのフレームにおける情報を保持しなくてもリプレイを再生することが容易に実現できます。

その一方で、順序付けすることにコストを要求するものもあります。ぎゃるぱにXでは、弾を敵より前に表示するなど、表示順序が重要でした。これを維持するためにリンクリストでオブジェクトを管理しているわけですが、このことはメモリの断片化(フラグメンテーション)を引き起こします。

セルを配列で確保しているとし、リンクリストにおいて順番に使用されている、つまり断片化されていない状態にあるとします(図1)。これは、オブジェクトが一回も破棄されていない状態に相当します。


図1:断片化の起こっていないリンクリスト

ここで、2 番目のセルが破棄されるとリンクリストのセルは 1 番目から 3 番目に接続されます(図2)。


図2:セルを破棄したリンクリスト

新しいオブジェクトが生成され、セルが再利用されると、4 番目から 2 番目に接続されます(図3)。これが断片化です。


図3:断片化したリンクリスト

オブジェクトの生成と破棄が繰り返されると、リンクリストの接続状況は悪化の一途をたどります。これに伴い、メモリアクセスはランダムアクセスとなり、キャッシュ機構を持つシステムでは不利な状況へとなっていきます。

順序なしリスト

コストを必要とするリンクリストですが、条件付きならメモリの断片化からおさらばすることができます。

話は簡単です。オブジェクトの破棄による空きセルが後ろにつくから断片化が起こるのであって、セルの順序を維持したまま再利用すればいいのです。オブジェクトの順序を保持したまま実装するには、破棄したセルより後方のセルをすべて複写する必要があります(図4)。これでは先頭のセルを破棄した場合に、すべてのセルを複写しなければならなく、オブジェクト数が増えた場合に実用的でありません。


図4:セルの複写による断片化の回避

ここで、オブジェクトの順序を保証しなくてよければ、オブジェクトの破棄による空きセルに適当なオブジェクトを突っ込むことで断片化が避けられます。そうです、何も登場人物が死んでしまったからといって、同じポジションに新しいキャラを待つ必要は無く、すでにいる登場人物でも構わないのです。どこから持ってくるかが問題ですが、単純に最後尾とするのが自然です。その結果、千砂さんは水無瀬さんではなく一砂くんを選ぶことになったのです羊のうた (c)冬目景/幻冬舎2](図5)。


図5:千砂さんのラブラブ対象

 2羊のうた 羊のうた (c)冬目景/幻冬舎
和服好きなら絶対に外せない一冊。回を追うごとに柔らかくなっていく千砂さんの表情もそうですが、中学生の千砂さんがたまりません。吸血鬼の話なので(そうか?)、月姫にはまった人は楽しめるでしょう(本当か?)


実装

順序なしリストの実装は非常に簡単です。リストのセルを配列で確保しておきます。オブジェクトを追加する場合は、リスト最後尾の次のセルに対して初期化し、セル使用数を一つ増やします。オブジェクトの削除では、リスト最後尾のセルを削除するセルにコピーします。

これをプログラムにすると次のようになります。

// セルの定義
struct SCell cell[MAX_CELL];
int numcell = 0;

// オブジェクトの追加
cell[numcell].x = 320;
cell[numcell].y = 240;
numcell ++;

// すべてのオブジェクトの処理
int idx = 0;
while(idx < numcell)
{
if(process(&cell[idx]) == false)
{ // 削除処理
numcell --;
cell[idx] = cell[numcell];
}
else
idx ++;
}

これならバグも発生しにくいですね。とかいいつつ、コーディング、ミスりましたが(笑)

比較評価

順序なしリストはリンクリストと比較して、オブジェクトの廃棄時にメモリコピーを余分に行っています。しかし、これはオーバーヘッドとはなりません。なぜなら、書き込み先のデータは次に処理されるオブジェクトとなるからです。順序なしリストの利点は、最大オブジェクト数を多くとってもメモリの断片化による性能低下が起きないこと、リンクリストのための next ポインタが不要になることです。

ということで、同条件で速度に大きな違いが出るか、試してみます。計測は以下の条件で行います。メモリ断片化の影響を見るために多少現実離れしているのがポイントです(笑)。コードも頑張って書き下ろしましたですよ計測プログラム3]

 3計測プログラム:44-list.cpp (4824 bytes)


実行結果を表 1 および図 6 に示します。セル最大数が小さい場合はデータがキャッシュ内にあるため、差がありません。セル最大数を増やしていくと順序なしリストが明らかに高速となります。実際のゲームに使う場合は、グラフィック描画によってキャッシュは毎フレーム破棄されると考えられますので、より有効、かもしれません(弱っ

表1:計測結果(PentiumII 333MHz)
オブジェクト
最大数
リンクリスト
(msec)
順序なしリスト
(msec)
100228219
200376397
400734684
80017191800
160043503853
320092597659
64001903815275



図6:計測結果のグラフ(PentiumII 333MHz)
おわりに

一見すると利用法のなさそうな順序なしリストですが、そんなことはありません。2D のゲームにしても、各オブジェクトを 3D のポリゴンのテクスチャとして描画することとし、Z オーダで描画順序をコントロールすれば処理順序は重要でなくなります。

2D のゲームであってもアイデア次第で用途はあります。実際、ぎゃるぱにXでもある弾幕では順序なしリストを使用しています。使用用途が気になる人は通販でゲットだ!(笑)オブジェクトの New/Delete による負荷の回避、弾の最大数の確保といった点を考慮して弾幕を見ていれば、どの弾幕か一目でわかるんではないかと思います。

そんなこんなで意外に有用な順序なしリストですが、日常の作業の優先順位を順序なしリストで管理してはいけません。後から追加された現実逃避ばかりが先に処理されて、肝心の仕事がいつまでたっても処理されなくなります。
・・・。む、そうか。そういうことだったのか。それでわ。

Virtual Payment System100えん10えん1えんヘルプ

第45回キーリピート
はじめに

みなさん、フィギャー。ここのところ、どうもモデラづいてます。モデラといっても MS-Word についてくる Visual Modeller みたいにプログラムモジュールのモデリングではなく、プラモデルとかのモデリング方面です。最初はペーパークラフトだったのですが、次の月にはガンプラ、そしてその次はガレージキットのフィギュアへ進みつつあります。

なんでそうなったのか、いまいち判らないのですが、ドールマスタードールマスター (c)井原裕士/メディアワークス1] に大きく影響を受けたことは間違いありません。ドールマスターの久具津さんはまるでスケッチでもするみたいに作成しており、それを見て(読んで)これならと始めてみると、実際は最初から最後までやすりがけしているようなものです。だ、騙された!(笑)
 1ドールマスター ドールマスター (c)井原裕士/メディアワークス
電撃王で連載中のフィギュアモデラーのお話。雛子さんのキャラは言うまでもなく、ときおり垣間見せる久具津さんの圧倒的な造形力にもうメロメロ。妙な迫力や小気味よいテンポで進む展開は読む人を飽きさせません。早くも 3 巻が待ち遠しい・・・


そんなわけで、やすってやすってやすりまくっている今日この頃なのですが、反復作業は慣れるもので、段々作業が高速化されているようです。そもそも人間の感性は同じ刺激に対しては鈍くなる性質を持っているため、音の強さの単位であるデシベル(db)のような対数比による尺度が利用されることになります。また、感性だけでなく動作に関しても慣れの要素は大きな意味を持ち、たとえばユーザインタフェースでは初回の操作感もさることながら反復して行う場合には決まった手順を繰り返せるようにする必要があります。というわけで、Zwei! のアイテム移動は改善して欲しいところです。

パソコンにおいて、反復を前提とした場合のユーザインタフェースではキーボードが有利です。このページを読まれる方なら一定の操作時にキーを順序よく順番に押していくという経験があるんではないでしょうか。僕の場合、ノートパソコンをサスペンドするときに[CTRL]+[ESC]→[U]→[T]→[ENTER]の連続コンボを叩き込んでいます。

キーリピート

キーボードで繰り返し作業を行う場合、キーリピート機能は必須です。一応、説明しておくと、キーリピートとはキーを押しっぱなしにしておくと自動的にキーを連打したように入力が行われる機能のことです。

さて、キーリピートはキーボード以外にも適用できます。これをパッドでの操作に適用することでゲームにおいても快適なメニュー操作が可能となります。というか、キーリピートのないメニューはストレスの素です。

リピート無し入力処理

キー入力を実装する上で、最も簡単なのは残念なことにリピートをサポートしない方法です。この場合、キー入力に対する処理は、キーが押された瞬間だけです。これをチャートで示すと、キー入力によって信号が立ち上がったときで、これは yaneSDK では KEY_PUSH マクロでキャッチできます(図 1 )。

リピート無しのチャート
図1:リピート無しのチャート
定間隔リピート処理

リピートを実装する場合に、始めに思いつくのはカウンタを用いて一定間隔ごとに処理する方法です。この場合、キー入力に対する処理は以下の 2 つに対して起動されます(図 2 )。

  1. キーが押された瞬間
  2. キーが押されている状態において一定間隔

定間隔リピートのチャート
図2:定間隔リピートのチャート

チャートから処理を起動する条件は以下の 2 つになります。

  1. KEY_PUSH
  2. KEY_PRESS 状態において、カウンタが一定値
これをプログラムする場合は、次のようになります(リスト 1)。

リスト1:定間隔リピートのアルゴリズム
if(KEY_PRESS)
{
if(KEY_PUSH || counter == REPEAT_TIME)
{
// 処理
counter = 0;
}
else
counter ++;
}
else
counter = 0;
一般的リピート

定間隔でのリピートは実装が簡単ですが、よい操作感を得るのは困難です。リピート間隔が短い方が、慣れに伴う操作感の低下が起こらないのですが、リピート間隔を短くした場合、すばやくキーを離さないと意図に反して二文字分のキー入力が発生しかねません。とはいえ、リピート間隔を長くするとイライラすることになるわけです。

手元にあるキーボードで試してみればわかると思いますが、実際のキーリピートでは定間隔でのリピートではなく、リピートによるキー入力が始まるまでの時間が長めにとられています。このようなリピートは、カウンタに対する処理を工夫することで実装できます。まずは、カウンタについて考えることにします。

カウンタを使う場合、リスト 1 のようにカウンタの初期値とリセットしたときの値を等しくするとリミット値との比較が面倒になります。ここで、リセットするときの値にゲタを履かせると初回と二回目以降の間隔が異なるリピートが可能です(図 3 )。

ゲタを用いた不定間隔の生成
図3:ゲタを用いた不定間隔の生成

実際にプログラムする場合には、リピートが始まるまでの時間と、リピート間隔で指定する方が直感的なので、ダウンカウンタとして実装します。このカウンタとその他もろもろの情報からリピート処理を行います(図 4 )。

不定間隔リピートのチャート
図4:不定間隔リピートのチャート

チャートから、処理を起動する条件が次のときであることがわかります。

KEY_PUSH || (KEY_PRESS && counter == 0)

ここで、KEY_PUSH が立ち上がった場合、カウンタは 0 と初回のリピート時間のどちらの値として考えても構わない(プログラム順序によってどちらかが確定される)ことに注目すると、KEY_PRESS && counter == 0 の条件でも満足できることがわかります。このことから、プログラムは次のように書くことができます(リスト 2 )。

リスト2:ダウンカウンタによる不定間隔リピートのアルゴリズム
if(KEY_PRESS)
{
if(counter == 0)
{
// 処理
if(KEY_PUSH)
counter = FIRST_REPEAT;
else
counter = NORMAL_REPEAT;
}
else
counter --;
}
else
counter = 0;

ちなみに、マクロ化するのであれば、ちょっと工夫して書くといいかもしれません(リスト 3 )。これなら処理以外の部分を一つのマクロに置き換えられます。

リスト3:ダウンカウンタによる不定間隔リピートのアルゴリズム2
if(KEY_PUSH)
counter = FIRST_REPEAT + NORMAL_REPEAT;
else if(KEY_PRESS && counter == 0)
counter = NORMAL_REPEAT;
else if(counter > 0)
counter --;
if(counter == NORMAL_REPEAT || counter == (FIRST_REPEAT +NORMAL_REPEAT))
// 処理
加速リピート

いろいろなゲームでメニューやランキングを触っていると反面教師を含めてユーザインタフェースの勉強になるわけで、その一例としてリピート速度が加速するタイプを実装してみます。と言っても、カウンタにセットする値をいじるだけです。ここでは、カウンタのためのカウンタ、subcounter を用意しておきます。あとは、リピート間隔を subcounter の値に応じて変更することで加減速も朝飯前です。あぁ、腹減った(リスト 4 )。

リスト4:subcounter によるリピート間隔の変化
if(subcount < REPEAT_TIME -1) subcount ++;
count = REPEAT_TIME - subcount;

え?チャート?わかりにくいっしょ。で、操作感にどれほどの違いがあるかをテストするためにそれぞれの方法を実装してみました(図 5)。パラメータは適当に設定してありますが、うまく調整すれば実用レベルになるはずです。上下でリピートのモード切り替え、左右で文字選択です。試してみてくださいキーリピートテストプログラム2]

キーリピートのテストプログラム
図5:キーリピートのテストプログラム

 2キーリピートのテストプログラム:45-keyrepeat.lzh (66569 bytes)


おわりに

今回は、簡単のために一瞬で隣の文字へ移動しますが、滑らか〜に移動するようにすれば市販ソフトと同等の見栄えを演出できるようになります。メニューはよく操作するところなだけに、ストレスが溜まりやすいところでもあります。このような細かい部分を一つ一つ作りこんでいくことで作品全体のレベルアップが果たせるのです。

そんなわけで、作品全体の完成度を追求すべく、ほとんど見えない場所の合わせ目を消すためのやすりがけを繰り返す日々です。しゃこしゃこ。
それでわ。


参考文献
 ドールマスター (c)井原裕士/メディアワークス1 ドールマスター (c)井原裕士/メディアワークス
雛子さんのみょーなハイテンションでの百面相が楽しい。とりあえず、僕的にもツボです(1 巻 105P 参照)。読む人をフィギュア界に取り込む罪作りなタイトル。さぁ、僕と一緒にフィギュアを作ろう!
井原裕士さんのページぱら・さいと 〜井原裕士の小部屋〜

 キーリピートのテストプログラム2 キーリピートのテストプログラム:45-keyrepeat.lzh (66569 bytes)
紹介したリピートの実装例。コンパイルには yaneSDK 1.63 あたりが必要です。コンパイル方法の詳細は 第34回 を参照ください。
上下でリピートのモード選択、左右で文字選択、ESC で終了です。

Virtual Payment System100えん10えん1えんヘルプ

第46回How do 誘導?
はじめに

みなさん、ナエナエ〜。ガレージキットのナエさんを買ってきたはいいのですが、リアルタイムで見ていなかったために資料が全く無く、困っている今日この頃、いかがお過ごしでしょうか。幸いなことに、ビデオはリリースされているのでレンタルビデオ屋に行くわけですが・・・ねぇ!全然ねぇよ!ということで、レンタルビデオ屋をはしごする毎日でした。3 週間かけてようやく全巻見ることができました(笑)。

いちおう、メダロット魂のビデオは一般販売もされているようですが・・・って、1 巻が出て打ち切りですか!だめすぎる・・・

なんでこんなに血眼になって資料を探すかというと、今年の目標を達成するためですが、どんな目標を立てたかは秘密です。ともあれ、今年は目標に向かって邁進する覚悟です・・・って、3 月になって言うことではないような。

さて、目標に向かってと言えばホーミングなわけで、ホーミング系の弾幕を実装することは直進系の弾幕を実装するよりも、それなりにレベルの高い人でないと理解できないらしい(某氏談)ネタです。1]。そんなですから、みなさんも暁のコーダ部屋を読んでレヴェルアップしましょう!
 1...それなりにレベルの高い人...
ネタです、ネタ。そんなことはまったくないので誤解なきよう。というか、この言い回しは「おまえ、なに言ってっかわかんねぇよ!」と言っているようにしか思えんのですが、褒め言葉らしいです。世の中には不思議なことが多いですなぁ。


ホーミングの基本分類

ぎゃるぱにXにおいてホーミング系の実装は大きく次の二つに分類できます。

世にあるホーミングは二次元・三次元を問わずこの二つのどちらかで表せます。とか言いつつ、他にもあるんじゃないの〜?と懐疑的なので知っていたら教えてください。

回頭型ホーミング

回頭型は、弾の移動を速度と方向で表しておき、方向を目標方向へ変化させます(図 1 )。ミサイルやレーザーをホーミングさせる場合はほとんど回頭型となります。

回頭型ホーミングの基本動作
図1:回頭型ホーミングの基本動作

回頭型では、毎フレームの回頭角度(図 1 内 delta)の与え方がキモとなります。目標方向に応じて一定値を加える方法が簡単です。

if(目標が左側)
angle += ROTATE_VALUE;
else
angle -= ROTATE_VALUE;


ただし、この場合は蛇行する恐れがあるので実用的とは言えません。

実際のミサイルを参考にすると、一定時間内の最大回頭角度は決まっており、その中でなら自由に回頭できると考えることができます速度変化は無視します。2]。これをアルゴリズムに適用すると次のようになります。
 2厳密には摩擦による速度低下、バーナーによる増速、回頭によって生じる空気抵抗による速度低下などなどありますが、速度一定であるとしたときの話です。


delta = 目標方向 - angle;
if(delta < - 回頭最大値)
delta = - 回頭最大値;
else if(delta > 回頭最大値)
delta = 回頭最大値;
angle += delta;


実装してみるとわかるのですが、角度はラップアラウンド359 度の次に 0 度がくること3]するために最初の一行をどのように書くかが面倒です。問題が発生する例は現在の方向が 359 度、目標方向が 0 度の場合で、左へ回頭することができなく、358 度の方向へ回頭してしまいます。
 3角度を 0 〜 359 度の範囲で表した場合、359 度の次は 0 度になるという性質です。


で、正解の一つは次のように、回頭角度を正規化0 度から 359 度の範囲の値で表すこと4]して 180 度よりも大きい場合は反対回り、です。
 4...回頭角度を正規化...
角度を 0 〜 359 度の範囲で表すこと。この場合は、360 で割った余り、です。


delta = 正規化(目標方向 - angle);
if(delta > 180 度)
delta -= 360 度;


んで、実装してホーミングさせてみると図 2 のようになります。単純な回頭型は一定半径の円の組合せとなるような軌道を描くことになり、不自然に見えることがあります。これを解消する場合、速度を可変とする、速度に応じた回頭量に変化させる、などの方法があります。

回頭型ホーミングの実行例
図2:回頭型ホーミングの実行例
引力型ホーミング

引力型は、ヲタクが秋葉原に吸い寄せられるように、春香さんが優しい男に引き寄せられるように新暗行御史 (c)尹仁完・梁慶一/小学館5]移動するタイプで、弾の移動をベクトルで表しておき、目標方向に応じたベクトルを加算することで新しい移動ベクトルを得ます(図 3 )。手っ取り早く楕円軌道を得たい場合は引力型を使うと簡単・・・と言いたいところですが、パラメータ調整が結構微妙です。
 5新暗行御史 (c)尹仁完・梁慶一/小学館 新暗行御史 (c)尹仁完・梁慶一/小学館
サンデー GX で連載中。春香さんの最初のコスチュームは、そういう事情なのだろうなぁと思っていたら、それが標準服だったり。本編自体も面白いですが、ことあるごとの春香さんの表情が密かな楽しみ。


引力型ホーミングの基本動作
図3:引力型ホーミングの基本動作

引力型は、どれだけ引力の影響を受けるかがキモですが、その程度によらず図 3 のように移動方向と引力方向の成す角が 90 度以内の場合は加速します。あまり移動速度が速くなってしまうのも問題なので、最高速を設定する必要があります。

direction = 自機方向取得( );
atr_x = 引力影響率 * Cos(direction);
atr_y = 引力影響率 * Sin(direction);
mov_x += atr_x;
mov_y += atr_y;
if(mov_x*mov_x + mov_y*mov_y > 最高速*最高速)
{
a = ArcTan(mov_x, mov_y);
mov_x = 最高速 * Cos(a);
mov_y = 最高速 * Sin(a);
}


引力型ホーミングを実装した例を図 4 に示します。基本的に最高速制限は必須ですが、楕円軌道で最高速となるのは長軸と並行な状態なわけで、最高速を遅くしてしまうと弾を引きつけてよけることができなくなるので注意です。ちなみに、ものによっては最低速の制限もつけることも必要です。プログラム的には最高速の制限と同じですな。

引力型ホーミングの実行例
図4:引力型ホーミングの実行例

万有引力では距離が近いほど影響を受けますが、このコード例では、目標との距離にかかわらず一定の引力としています。万有引力ライクに実装する場合は、自機方向から引力を計算するのではなく、弾の位置から見た自機の座標をベクトル成分とし、係数をかけて引力とします。

実装例

ホーミング系の弾を実装する場合、画面外でのオブジェクト削除だけでなく、一定時間でホーミング動作を終了するようにしておく必要があります。そうでないと、いつまでたっても画面内でグルグルと回っている状態になりかねません。

引力型ホーミングの描く軌跡の楕円は円のうちってわけで、自機との関係や最高速・最低速の制限によっては円に近くなります。実際のホーミングを体感してみてください(図 5 )ホーミングテストプログラム6]
 6ホーミングのテストプログラム:46-homing.lzh (68749 bytes)


ホーミングのテストプログラムの実行例
図5:ホーミングのテストプログラムの実行例

ぎゃるぱにXで実際に使用しているホーミングはこの2種類を基本に多少のカスタマイズを行っていますが、パラメータをいろいろ変更するだけでも楽しめます。

おわりに

ホーミングは誘導弾だけでなく、オブジェクトを目的地まで移動させるのにも使えます。この場合、目的地に到達する保証がないので注意が必要です。そういえば、今年の目標のうちの一つが定期更新だったのですが、いきなり遅刻していまいました。・・・ってことはあれですか、僕の目標へ向かう力はホーミング程度ですか。

ちなみに、目的地の近傍を通り過ぎたホーミングは目的地が移動しない限り、到達できなかったりするんですが。
それでわ。

参考文献

 新暗行御史 (c)尹仁完・梁慶一/小学館5 新暗行御史 (c)尹仁完・梁慶一/小学館
サンデー GX で連載中。作者は韓国の方だそうで、マンガ大国日本の漫画家さんもうかうかしていられませんな。絵のクオリティが高く、展開も面白いので下手な日本マンガなど足元にも及びません。最初は「好みの絵じゃない」と遠慮していたのを都市夫くんが貸し付けてくれました。・・・ってゆーか、なんでもっと早く貸してくれなかったんだ!あなたも韓国マンガを読んでインターナショナルな人間へレヴェルアップ!春香さんを見ていると萌えは万国共通なのかと思ってしまいます(笑)

 ホーミングのテストプログラム6 ホーミングのテストプログラム:46-homing.lzh (68749 bytes)
紹介したホーミングの実装例。コンパイルには yaneSDK 1.63 あたりが必要です。コンパイル方法の詳細は 第34回 を参照ください。
カーソルキーで自機移動、Z で回頭型ホーミングを発射、X で引力型ホーミングを発射、ESC で終了です。

補箋(2002/05/21)

 Nobo さん:Nobo's Homepage
http://www.yk.rim.or.jp/~noboyama/
http://www.yk.rim.or.jp/~noboyama/program/game/bezier1.html
別件についてサーベイしていたところ、たまたま発見しました。ベジェを使っての軌道計算です。問題点がなくはないですが、検討の価値は十分すぎるほどにあります。実用されていたりするんでしょうか・・・

Virtual Payment System100えん10えん1えんヘルプ

第47回初歩のテーリング
はじめに

みなさん、こんにちわ。今年から WRC が地上波で放送されるようになってウハウハな今日この頃、いかがお過ごしでしょうか。放映時間はたったの 30 分なので中身は推して知るべしですが、多くは望みません。がんばれスパイク!そのためならラリーゲームの一つや二つ、いくらでも買いますよ!それがいかにラリーゲームとしては根本的な問題があったとしても!コドライバーのナビが遅すぎます1]
 1ラリーでは助手席に乗ったコドライバーが「100m 先左カーブ」とナビしてくれる内容でスピードとラインを決めてコーナーに突っ込みます。当然ながら「ヘアピン」と言われてからブレーキングすることになるのですが、・・・言うのが遅ぇよ!
でも、ラリーファンなら買いです。


実のところ、好きなのはラリーに限った話ではなく F1 は言うに及ばず、JGTC も当然で F1 がつまらなかった一頃は、バイクのバトルの熱さにはまっていたりしました。高校のときはわざわざ 10Km の道のりを電車通学せずに走破するぐらいで、自転車並木橋通りアオバ自転車店 (c)宮尾岳/少年画報社2] も大好きです。
 2並木橋通りアオバ自転車店 (c)宮尾岳/少年画報社 並木橋通りアオバ自転車店 (c)宮尾岳/少年画報社
自動車、バイクのマンガは数多しと言えど、自転車のマンガと言えば、これに決まり。アオバ自転車店を中心に、自転車と人との触れ合いを描いた作品、とは建て前で毎回の表紙に男が出てこないあたり、よくわかってます。これを読んでいると、自転車が欲しくなってきます。・・・思うツボだよ!


これらに共通するのは車輪がついていることで、まさに車輪は世紀の大発明(だっけか?)なのが実感できます。車輪のどこがすごいかと言うと、回転することによって摩擦面を無限の長さで得ることができる点です。これの延長上として無限軌道のキャタピラがあるわけですが、こちらは前後にしか動けないのに対し、車輪なら左右へもスライドで自由自在です(違

テーリング処理

車輪のスライドはタイヤ痕となって路面に刻み込まれます。車のゲームでドリフトするとタイヤ痕が残っていくのはみなさんご存知の通りです。サーキットを周回する場合はすべてのタイヤ痕を記憶するのが理想ですが、タイヤ痕が多くなればなるほど記憶するためのメモリ領域を必要です。パソコンのように事実上無限のメモリ領域を使えるのならいざ知らず、そうでない場合には見えなくなるぐらいまで保持しておけば十分です。

タイヤ痕というと車のゲームに限定されるように感じられますが、そんなことはなく、自機の動きをトレースするオプションやレーザーといった効果も同様の処理で扱うことが可能です。簡単のために第 46 回暁のコーダ部屋,第 46 回:How do 誘導?3]のホーミングとその残像について考えてみることにします。
 3暁のコーダ部屋,第 46 回:How do 誘導?
http://www.aya.or.jp/~sanami/peace/memorial/code41-50.html#CODE46


残像ちっくな動きを作るには、次の二つの方法があります方式名称は勝手に命名4]

チップ方式:残像ごとにオブジェクトを生成
バッファ方式:残像を発生するオブジェクトに各残像の情報を保持するバッファを用意

両者は一長一短なので、使用する場面に応じて選択することが重要です。
 4チップ方式、バッファ方式という名称は便宜上、独自に命名しています。


チップ方式

先頭のオブジェクトが一定間隔で残像オブジェクトを生成します。それぞれの残像はアニメおよび移動その他もろもろの処理が行われることになります。

利点
先頭のオブジェクトは、一定間隔で残像オブジェクトを生成するだけなため、プログラムが容易です。残像オブジェクト側もオブジェクトごとにアニメや移動などを実装されるため、プログラムの見通しがよくなります。

欠点
先頭のオブジェクトと残像オブジェクトが異なるオブジェクトになるため、残像が先頭の動きに依存するような場合(残像が先頭に引かれるような場合)には、特別な処理が必要です。見た目よりもオブジェクトを大量に消費するため、オブジェクトの最大数の問題やメモリフラグメンテーションによるパフォーマンスの低下に注意する必要があります。先頭オブジェクトと残像オブジェクト間の描画順序、残像オブジェクト同士の描画順序と生成順序が微妙な関係になるため(図 1 )、描画順序を制御する機構が必要です。

先頭と残像の生成および描画順序
図1:先頭と残像の生成および描画順序
バッファ方式

先頭と残像すべてを一つのオブジェクトで管理します。

利点
最低限の情報だけをバッファに保存することで無駄のないメモリ管理が可能です。単一のオブジェクトなため、描画の順序をプログラムとして固定することができます。先頭の軌跡をバッファに保存するため、残像がその軌跡をなぞるように移動できます。

欠点
残像の数だけバッファを必要とするため、オブジェクトサイズの肥大化につながります。残像を含むオブジェクトが複数となる場合、残像同士の前後関係が正しくなくなります(図 2 )。

消えかけの残像が新しい残像を消す例
図2:消えかけの残像が新しい残像を消す例

両者の方式は優劣をつけるべきものではありませんし、二つをマージした手法も考えられます。場面に応じて柔軟に適用するのがお得です。今回は、バッファ方式について考えていくことにします。チップ方式はまた後日に。

有限長のバッファ

前回のホーミングのように残像の最大表示数が一定の場合、(実用的な長さという意味で)有限長のバッファで処理できます。このとき、残像が移動するか移動しないかでバッファサイズが変わります。

残像が移動する場合
残像が移動する場合には、先頭の位置を毎フレーム記憶しておきます。残像の表示位置はバッファの何番目の要素、で決まります(図 3 )。毎フレーム、バッファの内容をスライドすると先頭が停止しているときに残像が先頭の位置に集まります。どこぞのゲームのオプションのようにする場合は、先頭の位置が停止しているフレームでは、バッファの内容をスライドしなければいいわけです。

残像が移動する場合のバッファと残像の関係
図3:残像が移動する場合のバッファと残像の関係

残像が移動しない場合
残像が移動しない場合には、二つのアプローチが考えられます。

後者で実現可能なことから分かるとおり、前者は無駄にバッファサイズを大きく取っています。というのも使わないデータまで律儀に保持しているからです。ということで、一般的に後者のアプローチが取られます、はずです。この場合、各残像の表示位置はバッファ内の一定位置に保持されます(図 4 )。

T=t<sub>i</sub>
(a) T=ti
T=t<sub>i+j</sub> (0 < j < F)
(b) T=ti+j (0 < j < F)
T=t<sub>i+F</sub>
(c) T=ti+F
図4:残像が移動しない場合のバッファと残像の関係

処理の手順は次のようになります。

  1. 先頭の位置を移動(図 4 .b、c )
  2. 残像を生成するフレームならバッファの内容をスライド(図 4 .c )
このとき、新しく生成する残像の位置は先頭の位置を複写します(図 4 .c )。

処理の優劣を判断する材料となるのは 2 の部分になるわけですが、前回のホーミングでは次のように記述しています。

for(int i = SHADE_ANIM_PATTERN -1; i > 0; i --)
{
my->x[i] = my->x[i -1];
my->y[i] = my->y[i -1];
}


アニメーションの滑らかさを重視する場合には、各残像をアニメーションの 1 フェーズに対応させます。これを独立にした場合、1 フレーム内に同一パターンが存在したり、連続するアニメーションパターンのうち 1 パターンが存在しないことが発生し、見た目にガクガク感として現れることになります。レーザーのように通常状態において同一パターンが複数存在する場合はその限りではありません。

無限長のバッファ

有限長のバッファを用いる際の問題としては、次の二つが考えられます。

バッファサイズの決定は、十分な大きさを確保するため、残像の最後のパターンが消滅するまでのフレーム数を求めることで解決できます。たとえば、一つのパターンを 10 フレーム表示するようなアニメーションが 5 パターン用意されているなら、

10[frame] x 5[pattern] = 50[frame]


となり、50 フレーム分のバッファを用意すれば十分です。オブジェクト管理のシステムやオブジェクト自体のコードによりますが、安全のためにオブジェクト生成時の 1 フレームとオブジェクト破棄時の 1 フレームを加えたサイズよりも大きくとると安心です。

みなし無限長バッファ
近年のパソコンは急速に高速化されており、バッファ内容のスライドによるオーバーヘッドが問題になるようなことは稀だと思いますが、なんにせよバッファの内容をスライドさせるたびにバッファサイズ -1 分の要素の複写処理が発生します。特に、残像が移動する場合は毎フレーム発生することになり、オブジェクト数が複数あるなら、その数分の処理が発生してくることになりますので、あまりバカにできません。

このメモリコピーを回避する手段として、十分に大きなバッファを確保し、参照位置を移動する方法が考えられます(図 5 )。実際には有限長にも関わらず、前提を満たしていれば事実上、バッファサイズを無限長とみなすことができるわけです。

みなし無限長バッファ
図5:みなし無限長バッファ

たとえば、ぎゃるぱにXでは各ステージのプレイ時間は 240 秒を超えませんから 240x60 フレーム分のバッファを用意することになります。仮に x、y 座標だけを保持するならバッファサイズは次式で求められます。

4[byte] x 2 x (240x60) [frame] = 115200[byte] = 112.5[Kbyte]


このサイズを見てのとおり、ちょっとした場合でも大量のメモリ領域を必要とします。また、ある瞬間に注目するとバッファ全体において有効な情報が保持される割合(使用中の要素数)は常に数 % 程度に過ぎません。たとえば、全アニメパターンを 50 フレームで終了するオブジェクトをぎゃるぱにXで実装した場合を考えると、

50[frame] / (240x60) [frame] * 100 = 0.35 %


となり、せっかく用意したバッファの 1 % も使っていません。いくらなんでも、これではもったいないお化けが出てきてしまいます。

リングバッファ
そのための実装法としてリングバッファがあります。リングバッファとは、バッファの両端を連結することで有限長のバッファを無限長として扱う手法です(図 6 )。環状になったバッファを回転させるか、バッファの参照位置をずらすかはどちらを基準に考えるかなので、概念的には一緒ですレジスタをリング状に配置して命令数を削減5]
 5RISC プロセッサには、いくつかのレジスタを一まとまりとしてリング状に配置し、リングを左右に移動する命令と組み合わせることで命令数を少なくするものがあります。


リングバッファの概念
図6:リングバッファの概念

現実のメモリは線形に並んでいるため、バッファをリング状に配置すると言っても、それは概念に過ぎません。これを実際にどのように実現するかを難しく言えば、群環論で論じられる、となると思いますが、簡単に言えば角度を 360 度で表現することと同じです。今、360 個の要素を 1 度に 1 つの要素を対応づけて( 0 度には 0 番目の要素を対応づける)環状のバッファを構築したとします。360 度= 0 度ですから、359 番目の要素の次には 0 番目の要素があります。

この環状バッファに対して n 番目の要素へアクセスすることを考えます。n の範囲は限定されていないとすると、n を 0 から 359 までの範囲で等しいものに置き換えなければなりません(正規化)。n 度にある要素が n 番目の要素であることを思い出せば、角度の正規化という問題になります。その手順は、と言えば n が 360 以上なら 359 以下になるまで 360 を引けばいいのでした。それを、360 で割った商( MOD 演算)、と一言で表現するのです。n が負の値の場合は、一旦正負を逆転してから考えると次のようになります。

(-((-n) % 360) +360) % 360


一回目の MOD 演算の結果が 0 、すなわち割り切れた場合を別条件として考えれば、二回目の MOD 演算は必要なくなります。各演算の結果の範囲に注意すると正規化は次のように行えます。

int normalize(int n)
{
n %= 360;
if(n < 0)
n += 360;
return n;
}


と、一般型として考えてきましたが、実際の場面で使う場合には要素数を 2 のべき乗とし、ビット演算を用いて正規化する方法が一般的と思います。この場合の正規化関数は、要素数を N = 1 << Nb (ただし Nb>1)として、次のように記述できます。

int normalize(int n)
{
return n & (N-1);
}


ここで注目すべきは負の数に対してもビットマスクによる MOD 演算が可能な点です。したがって、わざわざ関数にせず、マクロでも簡単に記述できます。残像のスライド部分は次のようになります。

int idx = Normalize(index-1);
my->x[index] = my->x[idx];
my->y[index] = my->y[idx];
index = idx;


スライド部分の処理が大幅に簡単になったかわり、描画時にはリングバッファのリングに沿って描画するため、次のようなことになります。

for(int i = SHADE_ANIM_PATTERN -1; i >= 0; i --)
{
int idx = Normalize(index+i);
charaPlane->Blt(my->x[idx], my->y[idx], srcrect[idx]);
}


多重リングバッファ(勝手に命名)
リングバッファ(配列 ring[ ] )において、n 番目の要素の次の要素にアクセスするには、以下のように記述されます。

ring[normalize(n +1)]


したがって、その次は・・・、その次は・・・と簡単に想像できると思いますが、毎回正規化するのも大変です。よく使う場合にはテンポラリ変数に保存するなども考えられますが、いつもそれが許される状況であることもありません。

通常、リングバッファを線形に並べると 1 つのバッファ(バッファ A とする)が繰り返されています(図 7 )。

バッファの並び
図7:バッファの並び

ここで、同じ内容のバッファをもう 1 つ用意し(バッファ B とする)、2 つのバッファを交互に並べます(図 8 )。バッファ A の i 番目の要素とバッファ B の i 番目の要素が等しくなるわけです。

二重リングバッファ
図8:二重リングバッファ

このとき、要素番号 n が正規化されているなら n+1 番目の要素は必ず二つのバッファのどちらかに含まれます。二つのバッファを 1 つの配列にバッファ A、バッファ B の順で並べているとすると、n+1 番目の要素にアクセスしたときに配列の中に納まることになり、正規化の必要がなくなります。

n+1 から n+2、n+3、・・・と増やしていくことを考え、これを n+m として表すことにします。バッファサイズを N とすれば、n は正規化されているため 0 ≦ n < N の範囲にあります。また、リングバッファを無限長バッファとして使用する場合は 1 周先の内容は別の内容であることを考えると、0 ≦ m < N が成り立ちます。したがって、0 ≦ n+m < 2N の範囲ですから、バッファを 2 つ並べただけで配列の外側へのアクセスを回避することができます。 m ≧ N でのアクセスが発生するのであれば、それはすなわちバッファサイズが小さすぎることを意味します。

次に、n-1 番目の要素にアクセスすることを考えます。上での議論を踏まえればバッファ C を用意して・・・となると思った人は授業妨害、グラウンド 10 周!論理的に考えれば次のようになります。

  1. 要素番号を n-m とすると 0 ≦ n,m < N 、すなわち -N < -m ≦ 0 。
  2. -N < -m ≦ 0 の各値に N を加えると、-N+N < -m+N ≦ 0+N 。
  3. m' = -(-m+N) とすると n-m' の範囲は
    最小値:n = 0、m' = 0 で n-m' = 0
    最大値:n = N-1、m' = -N (m = 0) で n-m' = 2N-1

0 ≦ n-m+N < 2N、つまり配列の外側へのアクセスがないことがわかるわけで、このことから、n-m のときは n-m+N としてアクセスすれば正規化の必要がなくなります。これを用いることで、スライド部、描画部は次のように記述できます。

my->x[index] = my->x[index +N] = my->x[index -1];
my->y[index] = my->y[index +N] = my->y[index -1];
index = Normalize(index +1);


for(int i = SHADE_ANIM_PATTERN -1; i >= 0; i --)
{
charaPlane->Blt(my->x[index +i], my->y[index +i], srcrect[index +i]);
}


おわりに

シングルのリングバッファと多重リングバッファの違いは、

となります。したがって、インデクス指定による参照が少なく抑えられるかが、どちらを用いるかの判断の分岐点になります。あとは、プログラムの見易さですか(そっちの方が重要な気もする)。

ところで、前回は定期更新に遅刻でしたが、今回を 4 月分と言い張るためには 5 月中の更新が必須となります。う〜ん、なんか、そーゆー運営形態、どっかで聞いたことが・・・

あ、そうか、あれだ。「自転車操業」だ。ワカバさんも納得。
それでわ。

参考文献

 WRC 〜ワールド・ラリー・チャンピオンシップ〜 (c)Spike Co.,Ltd.1 WRC 〜ワールド・ラリー・チャンピオンシップ〜 (c)Spike Co.,Ltd.
ドリフトアングルをつけすぎるとエンジンがストールしたり、コースアウトするとモンスターマシンがサニーよりも非力になったりと不満はありますが、画面はさすがに綺麗です。全体のボリュームも十分ですが、いくら壊れていても次のステージに移ると完全復活するので「壊してでも速く」になっているのは残念。あと、もう一つ難しいレベルがあれば長く楽しめるのですけど。
WRC 〜ワールド・ラリー・チャンピオンシップ〜のページ

 並木橋通りアオバ自転車店 (c)宮尾岳/少年画報社2 並木橋通りアオバ自転車店 (c)宮尾岳/少年画報社
YOUNGKING にて絶賛掲載中。病弱なワカバさんも捨てがたいですが、ミホさんの前向きっぷりに心が洗われます。この本に影響されて電車で 90 分の勤め先へ自転車通勤しようかと本気で画策中。

Virtual Payment System100えん10えん1えんヘルプ

第48回バーチャル大儲け計画
はじめに

みなさん、お久しぶり。気が付いてみたら前回の更新からずいぶん経っているようで、僕の過ごしている時間と実際の時間の流れは違う速度なんでしょうか。ちなみに僕の時間では前回の更新から 9 ヶ月経過しているわけで・・・い、忙しかったんです、きっと。とはいえ、わざわざサイトを訪問して頂いている方に忙しい忙しいなどと聞かせるのは、不愉快にさせるだけ(少なくとも僕は不愉快になる)なので、今後「忙しい」は自粛することにします。ということで、忙しくもなく半年以上も放置していたことになるのでした。

それではまずいので、放置の理由を他所に求めるために、原因の数々を考えてみるも、株価の値動きへの相関関係ほどにしかありません(つまり、ない。あ、いや、ありますよ?)。そこで、当暁のコーダ部屋では、このような「実際はないんだけど、そーだったらいーなー」的関係のことを「バーチャル」と表現することを提案したいと思います。これによって、「半年間の放置期間」は「半年間のバーチャル更新期間」にすげかえることができました。完璧(どこが

バーチャル更新のバーチャル原因

バーチャル更新のバーチャル原因、要するに言い訳ですが、最も有力な言い訳は、更新に対するモチベーション(やる気)です。忙しい、忙しいなどと言っても、それこそ寝食の時間を削りこむところまでつきつめれば更新のための時間ぐらい、なんとかなるってものです。問題は、その時間を作る気になるかという点で、これはモチベーションにかかっているわけです。したがって、モチベーションの回復・増強が更新の維持につながります。 さて、そもそものモチベーションはと言えば、何故サイトを開いたか、という話になりますが、これはソフトウェアの公開についての議論にも通ずるところです。しかし、モチベーションがどこから生まれるか、という問題は人それぞれですから、これらの議論は発散せざるを得ません。実際、コーダ部屋を更新するモチベーションがどこにあるかすら、僕にはわかりませんし、D5.がゲームをなぜ作るか、と言われても同様です。

ただ、モチベーションの起因についての議論が不毛であることは多くの人の認めるところですが、だからといって、モチベーション全般の議論が無駄であることにはなりません。すでに存在しているモチベーションを補強する方法についての議論は有効で、最初のモチベーションを単なる思い付きでなく、身のあるものに結び付ける上では非常に重要とも言えます。

モチベーションの補強方法

まずは何をされたら嬉しいか、から入りたいと思います。と言ったところで結論は単純で、なんらかの形でサービスの受け手(読者・利用者)からの反応(以下、レスポンス)が大部分を占めていることが確実です。このレスポンスには金銭や意見・感想なども含まれますので、これら以外の補強方法としてはサイト更新の手間を軽減するためのツール提供のようなものになります。

それでは、実際に実行に移されている以下の補強手法について、特徴と長所・短所を見ていきたいと思います。

  1. アクセスカウンタ
  2. Web ランキング
  3. 匿名感想
  4. web 投げ銭
  5. アクセス解析
  6. 検索エンジン
  7. 一筆啓上運動
  8. ダウンロード販売
  9. あゆ板
  10. イベントでの感想

1.アクセスカウンタ(Web ページ)
古典的ではありますが、それなりの効果が認められます。フリーソフトだとダウンロードカウンタにあたります。
長所:お手軽。数字の推移で評価を推し量ることも可能。
短所:数が多くなりすぎると補強効果が低くなりやすい。意見・感想の率が低い現実を確認させられる。

2.Web ランキング(Web ページ)
アクセスカウンタよりも積極的に評価を得る方法。そういえば、プログラム系の読み物で Web ランキングって見たことないですね。あっても参加しないけどな!否定する気はまったくありません。1]
長所:アクセス数とは独立な動的評価を得られる。順位による優越感に基づく補強効果。順位があがることによる宣伝効果。
短所:いやおうがなしに他人との比較・競争になる。異なる方向性を同じ土俵で評価。村意識にもつながる。低い順位は逆効果。
 1参加していない人間から見た印象で、否定するなどという気持ちは一切ありません。


3.匿名感想(Web ページ/ソフトウェア)
名を明かさずに意見・感想を言うことができる。
長所:名前を明かさずに一方的にメッセージを発することができる。メーラーを立ち上げずにすむ。
短所:実は、名前を明かさないことは重要でなかったり。当コーダ部屋では、2 〜 3 レス/年ぐらい。感想に対する返事の機会がない。

4.web 投げ銭(Web ページ)
テキストサイト界、ろじっくぱらだいすのワタナベさんが提案僕の偏見です。2]。提案の文章では根本的なモチベーションとして金銭の取得として論じられているが、補強の手段として有効。
長所:お金という即物的な評価、実益。ルールの明確化による将来への責任の回避。
短所:まだまだ投げ銭側の手間が大きい。金銭の受け取りによる義務感の発生。守銭奴的イメージの発生僕の偏見です。3]
 2ワタナベさん:ろじっくぱらだいす
http://www.juno.dti.ne.jp/~logicp/index.html
ろじっくぱらだいす内:Web 投げ銭の投げ方
http://www.juno.dti.ne.jp/~logicp/zaregoto/nagesen/give.html
 3僕が採用することを考えたときのイメージであり、まったくの偏見ですし、否定するとかいう意図は全くありません。自分の文章がどれくらいのお金になるかは興味ありますが、実際の金稼ぎにはしたいと思わないんですよね。


5.アクセス解析(Web ページ)
主にリンク元ページが有効。日記ページからリンクされている場合は、率直な感想を得ることができるが、同時に脊髄反射で書かれた批判的な意見も多いので注意が必要批判的意見も参考にしてます。4]
長所:お手軽。リンクのほとんどを捕捉可能。
短所:ログが流れない程度に定期的なチェックが必要。
 4D5.と僕は批判的な内容も含めて楽しんでますので遠慮なく好き勝手書いてください。リンク張られている時点で嬉しいですし。


6.検索エンジン(Web ページ/ソフトウェア)
ページ名、ソフト名を検索。検索エンジンによって結果が微妙に異なるので、いろいろなところで試すと良い。infoseek はまったく関係ない単語で拾ってくるので困る。こちらも批判的な内容を拾ってくることが多いので注意が必要。批判自体は参考になるので問題ないですが、つまらない文章のネタで使われるだけ、ってのは閉口してしまいますな僕個人の感情的な意見です。5]
長所:お手軽。リンクが張ってなくても捕捉可能。
短所:他と識別できる程度の名前をつけておく必要がある。キャッシュが見れない場合は内容が流れてしまっている場合も。
 5D5.と僕は批判的な内容も含めて楽しんでますので遠慮なく好き勝手書いてください。リンク張られている時点で嬉しいですし。


7.一筆啓上運動(ソフトウェア)
Palm 界、パーム航空の機長さんが提案パーム航空、いっぴつけいじょう。6]。ソフトウェアをダウンロードしたら簡単なメールを作者へ送ろう、という運動。
長所:ルール化による半強制力提案では一切の強制をしていません。7]に基づいたレスポンス数の確保。ルール化による簡素なメールの正当化によりレスポンスの支援。
短所:それでも、まだ、敷居が高い。性善説。受け取る側の返信レスをルール化していないため、人によっては負担増の可能性も。
 6機長さん:パーム航空
http://palm.org/
パーム航空内 PAL071便:いっぴつけいじょう。
http://palm.org/f_pal_classic/f_pal_flight_000/p071_freedom.html
 7提案では一切の強制をしていません。


8.ダウンロード販売(ソフトウェア)
特定サイトにおいて、ソフトウェアをダウンロード販売。広義では同人ショップでの販売も含まれる、か。直接的な利益による補強よりも、話題にされることによって他手法での補強を期待できる。
長所:実益。
短所:実は、全然売れない。

9.あゆ板(ソフトウェア)
ダウンロード時に書き込みをしないとダウンロードURLが表示されない CGI 。一般人は見たことがない。
長所:利用者のレスポンスによってダウンロードが可能になるため、レスポンス数とダウンロード数はほぼ等しくなる。
短所:ダウンロード対象を得る時点でのレスポンスなので、「いただきます」「ありがとうございます」と言ったコメントがほとんど。そもそも、製作者がアップすることはないので、第三者が礼賛を受けることに。バカもおだてりゃ木に登る。そーいえば、ゆーとくんは今ごろどうしているだろうか。

10.イベントでの感想(Web ページ/ソフトウェア)
イベントで本人に直接簡単な感想・励ましの言葉をかける言われて複雑なことも・・・8]
長所:レスポンスする側の満足感、達成感が大きい。
短所:ブースにずっといなければならない。具体的な指摘はもらえない、もらっても思い出せない。時々反応に悩む感想も。
 8よく言われるけど言われて困る言葉 No.1 は「いつも読んでます!よくわかんないけどすごいです!」です。コーダ部屋は説明の文章で独自性を打ち出しているため、「よくわからない」=「お前の文章は全然意味不明」を意味することもありえるわけで、すごく複雑な気分(笑)。


暁のコーダ部屋における場合

当コーダ部屋では、アクセスカウンタ、匿名感想、アクセス解析、検索エンジンの他に[面白かった][つまらなかった]の評価システムを実装したことがあります。このシステム、通算でパーセンテージを弾き出すとサンプル数が多くなればなるほど収束していき、1つの評価の重みが少なくなりますし、逆に近傍の一定サンプルから弾き出すと簡単に 100 %になるという、どうしようもない結果に落ち着きました。

匿名感想については、返事や反論をする場がありませんので、機能しているとは言いがたい状況なのが現実です。書いた人は、読まれているかわかりませんし、書かれた側は返事したくてもコンタクトできない、と。ついでに公開していいものか、という問題もあります。

以上のことから、コーダ部屋における理想的な補強手法の仕様を浮かび上がらせることができます。

  1. レスポンスする側もレスポンスを得られなければならない。
  2. レスポンスする側に金銭的な負担を発生させない。
  3. レスポンスする際の手間は限りなく 0 に近い。
  4. レスポンス数に対する負担が発生しない。

これらの問題をクリアする手法について提案・実装したいと思います。

提案する手法

提案する手法は、各回の記事について金額を支払うことで評価を行います。この金額の累計を表示することで、提供側と評価側の両方にレスポンスを提供します。これで a がクリアできます。

次に、この支払い時の金銭をバーチャル化します。このバーチャル化により b がクリアされ、「金払ってんだから文句言わせろ」的意見を排除することが可能となります。

金銭のやり取りをバーチャル化することで、全体を CGI 等で扱うことが容易になるので、システム全体を CGI プログラムで実装することとします。これにより c がクリアできます。振り込み時にはページ上のボタンをクリックするだけで支払いが完了します。

CGI プログラムで自動的に処理することにより、レスポンスに対する作業の手間という負担を回避することができます。また、支払いは金額の累計として表れるのみですから、レスポンス数の増大は累計金額が増えるだけであり、一切の負担は生じません。これで d もクリアされました。

提案するシステムをまとめると、以下のようになります。

実装

コーダ部屋が置いてある Aya インターネットでは SSI が使えませんので、金額の出力は PNG 画像とします。したがって、HTML は出力となる PNG 画像を img タグで使えばいいだけになります。

CGI プログラムの処理は以下のようになります。

  1. 金額リストファイルの読込み
  2. 指定の回に金額を加算
  3. 金額リストファイルの書込み
  4. PNG ファイルの出力
  5. 支払い完了の通知

一定金額をただ支払うのではあまり面白くありませんので、記事の内容に応じて支払いの金額を変えられるようにしたいと思います。ただし、突拍子もない金額を支払われては企画自体の価値が無になりますので、1 円、10 円、100 円の 3 つから選べることにします。この金額は僕が自分の書いた文章を売りつけるときにあり得る金額、ということになります。これをボタンのイメージによって選択してもらうことにします(図 1 )。初めての人が悩まなくて済むように、ヘルプボタンを用意し、これはバーチャル支払いシステムの説明へのリンクとしておきます。

支払いボタン
図1:支払いボタン

CGI プログラムに渡すべきデータは、第何回か、およびいくら支払うか、の 2 つのデータですが、ついでにチェックコードまで含めてみます。チェックコードには PNG で使っている CRC のルーチンがあるので、これを使います。支払いのためのリンクは次のようになります(リスト 1 )。

リスト1:支払い用リンクの書式
<a href="price.cgi?支払いコード">


支払いコードは CRC によって算出されたコード部を含んでいるため、手作業で計算するのは非常に手間です。どうせ CGI プログラムでチェックルーチンを実装するのですから、支払いコードも出力できるようにしてしまいましょう。ということで、0 〜 1023 の数値が与えられた場合は支払いコード生成モード、それ以外の場合は支払い手続きに入ることとします(図 2 )当ページではカスタマイズしています。9]
 9なーんて説明してますが、第 3 者に好き勝手されては困るので当ページではこの機能、殺してあります。CRC 算出もカスタム化しておくのは基本です。


支払いシステムのフロー
図2:支払いシステムのフロー

出力となる PNG 画像は、文字のドットパターンを配列で持ち、これをもとに再構成します。いろいろな大きさで試してみましたが、固定幅ではどうにも格好がつかないため、数字はすべて当幅とした上でプロポーショナルにしました。文字ごとに送り幅を持っています(リスト 2 )。このデータの出力を図 3 に示します。

リスト2:文字パターンのデータ
@letter_pattern = (

0x78,0x84, ... ,0x78,0x00, #0

0x20,0xe0, ... ,0xf8,0x00, #1

0x78,0x84, ... ,0xfc,0x00, #2

0x78,0x84, ... ,0x78,0x00, #3

0x08,0x18, ... ,0x08,0x00, #4

0x7c,0x40, ... ,0x78,0x00, #5

0x38,0x40, ... ,0x78,0x00, #6

0xfc,0x04, ... ,0x20,0x00, #7

0x78,0x84, ... ,0x78,0x00, #8

0x78,0x84, ... ,0x70,0x00, #9


0x82,0x44, ... ,0x10,0x00, #\

0x00,0x00, ... ,0x00,0x00, #-

0x00,0x00, ... ,0x40,0x80, #,

);

@letter_skip = (7,7,7,7,7,7,7,7,7,7,10,16,3);


支払い累計金額の出力例
図3:支払い累計金額の出力例

以上のような CGI プログラムを作成、使用することにします。使いたい方はご自由にどうぞバーチャル支払いシステム CGI プログラム10]
 10バーチャル支払いシステムの CGI プログラム:48-pricing.lzh (4915 bytes)


おわりに

そんなわけで設置したので、気が向いたら評価してみてください。

このシステムでは同一人物が連続で評価することも可能にしてありますが、それを実行するにはそれなりの努力が必要ということで、有効にしてあります。 つまり、1000円振り込むためには、100円を10回繰り返す動作が必要なわけで、そうまでして振込みたいその気分を無駄にするのは間違い、と。あ、F5アタックしないでください。

こんなシステムの導入を考えながら冬コミを迎えていたのですが、夏コミで数十人からあった「ホームページ見てます」という声も、今回はありませんでした。やはり、評価されるには努力が必要ってことなのですね。
それでわ。

Virtual Payment System100えん10えん1えんヘルプ


バーチャル支払いシステム
暁のコーダ部屋のそれぞれの記事が面白い、有用だと思われた場合、対価として適当と思った金額をクリックします。これによって、あなたはバーチャルにお金を支払ったことになり(つまり、支払ってない)、そのバーチャルお金はバーチャルに僕の口座へ振り込まれるという、誰のふところも痛まない、いいシステムなのです。


sanami@aya.or.jp