行う - みる会図書館


検索対象: 月刊 C MAGAZINE 2001年4月号
129件見つかりました。

1. 月刊 C MAGAZINE 2001年4月号

て便利です。 ■キャラクタに決めた動きをさせる 敵キャラクタや弾の個性を決めるのが , この部分です。移動状態を提供するスクリ こでは「あ プト的なものもいいのですが るキャラクタに追尾」など , どのような動 きにも対応させたいので , 1 フレームに 1 回 , 関数を呼び出し , キャラクタの移動座標に その値を与えるような形にしておきます。 なお , 弾のように小さい物体が連続して たくさん出るような場合 , ある程度前の弾 が動いてから , 次の弾を出すという仕組み が必要です。そうしないと弾が数珠つなぎ になってしまいます。 ・自機の操作 Wonde 「 Witch によるプログラム開発で , 意 外と環境の構築に手間取る方が多いようで こで筆者の一例を示しておきます。筆 す。 者はノートパソコン (Panasonic Let's note C F-M32) と Windows 98 の組み合わせで開発環 境を構築しています。 プログラムを書くためには , ソースコード を入力するための「エデイタ」を用意してく ださい。個人的には市販ソフトの WZ Editor を使っていますが , フリーソフトやシェアウ ェアでも , いろいろなものが公開されていま す。プレーンなテキストファイルを出力でき て自分の好みが合えばどんなエデイタでもか まいません。 なお , メモ帳など一部のテキストエデイタ を使っている方は , 「 f00. c. txt 」のようなファ イル名にされてしまうことがあります。こん なときは工クスプローラでファイルの拡張子 を表示する設定にすると , 表示も名前の変更 一方 , 自機のほうはプレイヤが操作する ものですから , ボタンなどの状態を見てか ら座標の値を操作する必要があります。 1 フレーム中に 1 回はボタンを見て , それに 応じた処理を行うようにしておきます。 ■敵キャラクタを決めた位置に出現 敵キャラクタはゲームが進むことで特定 のタイミングで登場します。これを実現す るには , 前述のゲーム時間から判断するよ うにします。どのキャラクタを出すかにつ いては , 出現順に , 「登場するゲーム時間」 と「キャラクタの種類」の 2 つをベアにして 並べた配列を用意し , これをゲーム時間に 応じて読み出すようにします。 キャラクタの当たり判定 当たり判定は昔からさまざまな方法が考 えられていますが , こではそのうちのい くっかを紹介します。 ・移動の方向で調べる キャラクタがぶつかるということは「現 開発環境の構築 も行えるようになります。工クスプローラか ら [ 表示 ] → [ オプション ] で表示される「表示 タブ」で該当するチェックポックスを変更し てください。ほかにもコマンドプロンプトか ら「 en コマンドを使って「 ren f00. c. txt f00. c 」な どとして名前を変更する方法もあります。 ソースコードのコンパイルは , WonderWi tch 付属の makefile を元に LSI C-86 の kmmake を使ってコンパイル作業を行っています。ま た , 本連載の第 1 回で説明した Sen 引 t などの 転送ツールを利用して , できるだけ kmmake コマンド 1 つだけでコンパイルからテストま でを行えるようにしています。このあたりの 作業は繰り返し何度も行うものですから , 簡 単にできるように整備しておくべきでしょ つ。 コマンドプロンプトは昔から愛用している histo 「 y を使って , ファイル名補完や履歴機能 によるコマンドの再入力などをしています。 コラム 1 遊びあしイビ 血飜 i 忙 在移動している方向の先に弾や敵機がある」 ということにもなります。それぞれの移動 方向が重なったときは , それらがぶつかっ たと判断することができます。 べクトルを計算で求めて , それが重なっ たかどうかを調べるのが一般的なプログラ ミング方法です。 ・座標の重なり具合で調べる キャラクタが重なったということは , キ ャラクタの置かれている座標が重なってい ることになります。たとえば , 8X8 ドットの 大きさを持つキャラクタで , X = 20 , Y = 20 の 位置にあるキャラクタと X = 25 , Y = 22 にある キャラクタでは , X = 25 ~ 28 , Y = 22 ~ 28 の部 分が重なっています。 実際のプログラミングとしては XI , YI と X2, Y2 があった場合 , if (((xl く (2) & & 再コンパイルなどでキー入力の手間が減らせ て便利です。ほかにも Windows 98 系の OS で は標準で付いてくる doskey や , Windows NT 系の OS ではテフォルトで履歴管理が行えま す。類似のツールもインターネット上で多く 公開されているので , これもいろいろと試し てみてください。 コンパイラからのエラー出力などは 1 画面 に収まらないことがあります。そのときは , そのままリダイレクト (kmmake>foo, kmm akel more など ) して読んでいます。もちろん Windows NT 系では収まらない部分をスクロ ールすればあふれた画面表示を見ることがで きます。 ざっとこんな感じですが , 困ったことにあ まり不自由はしていません。いろいろなツー ルを組み合わせて自分だけの便利な開発環境 を作り上げてみてください。それもまた楽し みの 1 つです。 遊びのレシピ fo 「 WonderWitch 1 33

2. 月刊 C MAGAZINE 2001年4月号

定できます。実際のプログラミング例につ いては本連載の第 1 回 ( 2000 年 12 月号 ) をご 覧ください。また , 使い方については , Fi g. 3 にまとめました。 重ね合わせの処理は , 画像に指定されて いる透明色 ( パレットの 0 番 , というように 決められている ) で塗りつぶすことで , そ の部分が透過してバックにある背景となり ます。マスクを作ったり , 論理演算をした ・・といった手間はいりません。 スプライトも画像データを 8X8 ドット単 位で扱います。どんなに大きなキャラクタ でも , この 8X8 ドットの大きさに分割し , キャラクタフォントとして扱うことになり ます。もし , こうした大きなキャラクタの 座標を移動させるときは , それぞれ分割し て登録したスプライトに対して行うことに なります。 ■アニメーション 表示するキャラクタがただの 1 枚の絵だ ったら , あまり臨場感がありません。そこ で前述のように , あるタイミングで絵を少 Fig. 3 スプライトの使い方 しずつ変化させることで , アニメーション させてみることにします。 画像はアニメーションさせるぶんだけ用 意します。キャラクタの場合 , 最初と最後 がつながるようにしておいたほうが無難で しよう。この 1 枚当たりの画像を「コマ」な どといいます。 WonderWitch では画面サイ ズが小さいので , それほど細かくアニメー ション ( コマ割り ) させても , あまり効果は ありません。適度に大きな動きをさせたほ うが効果的です 表示の差し替えは , 単純にキャラクタフ オントを書き換えるだけでも十分です。た だ , そのままでは画面書き換え中のようす が表示されることがあり , 画面のちらっき などの原因となることがあります。そこで , いわゆる垂直帰線待ちを行うことでこれを 防ぎます。 WonderWitch では , sys_wait ( 1 ) で待つほかに , sys_get_tick_count ( ) で得た 値が変化したかどうかで調べる方法があり ます。 アニメーションでは , あるコマを表示し たあと , 次のコマまである時間だけ待つ必 1 . font ー set ー colo 「 data ( ) などで画像データをキャラクタフォントへ登録 2. sprite-set-char() にキャラクタフォントの番号を与え , スプライトとキャラクタフォントを 結び付ける 3. sprite-set—range() で登録したスプライトの番号の最初と終わりを指定し , 表示を行うよう にする 4. sprite-set 」 ocation() でスプライトの表示位置を指定する for (i = 最初のスプライ土く最後のスプライ土十十 ) { sprite-set-location(), 表示座標の横位置 + ()i 宅キャラクタの横の大きさ ) * 8 ) , 表示座標の縦位置 + ( ( 土 / キャラクタの縦の大きさ ) * 8 5. 移動させるときは sp 「 ite ー set 」 ocation ( ) に 1 ドット単位で指定。相対座標で指定するときは , 下記のようにする fo て (i = 最初のスプライ土く最後のスプライ土十十 ) { adr = sprite—get—l ocation(i); sprite-set-location(i , 相対座標の横位置 + (adr & 0xff) , 相対座標の縦位置 + (adr > > 8 ) 遊ひ。レイピ 要があります。この「 1 コマ当たりの時間」 を待っことでアニメーションの速さを調整 しているのです。 遊びのレシピ fo 「 WonderWitch 1 31 これは後述するキャラクタの出現や移動に を「ゲーム時間」などということがあります。 で進んだか」を知ることができます。これ りカウンタを 1 つ進めると「ゲームがどこま 1 つのカウンタを用意し , 1 フレームあた ・ゲーム時間 う処理にします。 らいくつ過ぎたら次のフレームへ移るとい sys—get—tick—count ( ) の値を得て , そこか 中に 1 フレームぶんの処理を連ねておき , するには , ゲームが終わるまで続くループ WonderW1tch でこのフレーム処理を実現 に合わせて表示することになります。 ションの実現などもこの連続するフレーム 理を終わらせる必要があります。アニメー 単位時間当たり , つまり 1 フレーム中に処 ると , 表示されるすべてのものは , この 1 換える」作業が必要になります。逆に考え 決まった時間ごとにキャラクタなどを書き は , 滑らかな画像表示をするために「ある くのキャラクタが動いて表示される画面で アニメーションのコマもそうですが , 多 ・フレーム を中心にちょっと寄り道してみます。 す。それが時間処理です。前述のフレーム はキャラクタ管理以外にもう 1 つありま シューティングゲームでたいせつな処理 ある時間ごとに処理を行う ておきましよう。 方法を紹介する前に , 少しその処理を考え 理方法や流れを決めています。ほかの処理 は , 時間に関する処理があらゆるものの処 てきたようです。シューティングゲームで どうやら時間に関する処理が必要になっ

3. 月刊 C MAGAZINE 2001年4月号

困ったが , そういえばほかの人も困ってい の内容には触らずに終了させれば問題は発 では一体何が気になるのか ? アンロッ クしてからクローズするまでのわずかな間 ると思うわけで , 一体Ⅲ MNET ではロック 生しないはず。では , オープンに成功し , 処理を CGI で書く場合にどうやって実現し ロックもできたし , 必要な処理もできたと である。これも非現実的かもしれないが アンロックしたその次の瞬間に何かほか しよう。次にどうすればいい ? ているのだろうか ? そういえば , CGI を作るには , 確実に動 手順としては , オープン , ロック , 処理 , のプロセスがロックしにくるかもしれない。 作状況を把握できるサーバがあると便利で アンロック , クローズ , というのが構造的 ロックに成功したら , その次には同じファ に美しいと思う。ファイルを 2 個オープン イルをオープンして上書きする可能性もあ ある。工ラーが発生したときに , http サー バのエラーログを確認すれば , 原因がはっ る。これではロックした意味がない。 するとき , A, B の順にオープンしたら , B というわけで , 実は現在使っている CGI きりすることがよくあるからだ。ただ , レ A の順にクローズすると何となく美しいの ンタルサーバは別として , CGI が使えるプ スクリプトは , オープン , ロック , 処理 , と同じだ。 List 1 のような感じである。 ロバイダでも httpd のエラーログまで公開 こでは構造がわかりやすいようにあえてイ クローズという順に処理している。 List 3 してくれるプロバイダは少ないようだ ( と ンデントしてあるが , 通常はこの処理では は実際のコードである。見てのとおり , ア いうか , あるのか ? ) 。 インデントを付けないことに注意するよう ンロックの処理はコメントアウトしてあ そこで目をつけたのが , 自宅で稼働して に。これに対して , List 2 のような書き方 る。つまり , アンロックしていないのだ。 いる叩 ache サーバだ。これなら , すべての サンプルにないことをすると , どうも精神 は , IN の有効期間と OUT の有効期間が交 的には落ち着かない。はたして本当にいい 情報をチェックできる。というか , もしか 錯しているので , 何となく美しくないよう して , これで動作したものをそのまま公開 のだろうか ? な気がする。 すれば , ロックの問題は解決するのでは ? ただ , この順序にまったく意味がないか んなでかき、をかけると ? どうせフレツツ ISDN で 24 時間接続してい というと , あまり現実的ではないかもしれ るのだから , 外からアクセスできないこと ないが , 理屈だけなら多少のこじつけは可 こうやってとりあえず CGI は完成した もない。という経緯があって , 現在は自宅 能である。たとえば , $ infile をオープンし というか未完成だが , とにかく動く一 のサーバで機能の一部を公開して使ってい ている時間をできるだけ短くしたい場合。 る。あまりトラフィックが多くなっては使 にはなっている。そこで , これを RIMNET ほんのわずかではあるが , List1 のほうが い物にならないと思われるが , アンケート に転送して処理させようとした。ところが , $outfile をクローズするのに必要な時間だ ここに大きな落とし穴があったのだ。 RIM のための CGI 処理に絞っているので , これ け , $ infile をオープンしている時間を短縮 NET では flock は正しく動作しないというの ならどうせ 1 日の利用者は多くて数名程度 することができる。あるいは , $outfile に である。公式アナウンスによれば , 「 PerI なので , まったく問題にならない。 対して行う処理が , 時間がかかるものなら の flock 関数など , OS が提供するファイル というか , 考えてみれば , そんなに利用 ば , 最後に $ infile を読んだらすぐにクロー 者が少ないのなら , わざわざロックしなく の 1 。 ck 機能は , 複数のサーバが並列動作を ズしてしまえば , そのあと時間のかかる処 ていいような気がするのだが しているリムネットの WWW サーバでは正 理をしてから $outfile をクローズするまで ( フィンローダ @nifty FPROG SYSOP) しく動作しません。」というのだ。これには の時間が短縮できる可能性はある。 きを開けるタイミング 現在使っているコード # # # # # # # # ロック処理を行いつつ必要なファイルをオープンする # $data は既存でなければならない さて , オープン , ロック , 処理 , クロー &error—exit("cannot open $data" ) unless (open IO, ″十く $data" &error—exit( ncannot lock $data" ) if (flock( IO, $LOCK—EX) = = FALSE); ズ , アンロック , という順序は , たぶん , &error—exit( "cannot open 引 og n つ unless (open LOG' ">>$ ー og ” &error—exit( "cannot lock 引 og 基 n ” ) if (flock(LOG, $LOCK—EX) = = FALSE); 原理的におかしい。というか , ファイルが p て土 n しも "$sys—date%n" クローズされた時点でロックは解除されて # # # # # # メインの処理 いると思われるので , たぶんアンロックで &body; きないのではないか ? だから , もしアン # # # # # # # # 後始末をする # flock IO, $LOCK—UN; ロックするのなら , 順序としてはオープン , close IO; # flock LOG, $LOCK—UN; ロック , 処理 , アンロック , クローズの順 close l.m; でなければならない。 List 151 ーダのあつばれご意見番 フィンロ

4. 月刊 C MAGAZINE 2001年4月号

00 フログラミング入門 を示します。 if(!a) ょっと話し始めた 5 ~ 6 歳といった感じでし ようか。 このべースさえきちんとイメー ジとしてとらえられれば , 第 1 段階はとり if (a==o) あえずクリアということになります。 は同じ意味になります。前者は厳密にいう と「変数 a の値が真 ( 0 以外 ) でなければ , つ 今回の内容により , 例題の一部を自分な まり 0 ならば条件に当てはまった場合の処 今回は C 言語のだいたいのイメージをつ りに変更して C 言語をどんどん体感してみ 理を行う」ということを示しています。前 かむことができるように , 覚えておいてほ てください。実際に動作させていろいろな ことを感じ取ることがとても大切なのです。 者のほうは書き方として簡潔といえますが しいことの最低限をまとめました。これだ 後者のほうが直感的に意味を理解しやすい け覚えておけば , 最低でも動作しているこ 今後はこの全体的な内容から少しずつ掘り とが目で確認できるようなプログラムを作 下げて , みなさんと一緒に , 徐々に C 言語 と , それぞれに長所があるといえます。ほ かの演算子については , List8 に簡単な例 成することができます。語学でいえば , ち に取り組んでいこうと思います。 条件文を使ったプログラム 1 / * 成績を判定するプログラム * / 2 #include く std 土 0. h> 3 土 n ( ) int tensu ー 5 6 7 tensu=55; if(tensu>80) { p て土 ntf に成績は A です。 n ” 11 else if(tensu>60){ 2 p て加 tf ( ”成績は B です。 3 14 else{ 15 p て土 ntf ( ”成績は c です。” 16 最後に List ー当 ensu ' ' という変数に 55 というデータを格納する もし変数 tensu の値が 80 より大きいなら」次の処理を行う、 成績は A です。”と画面に出力する もし変数 tensu の値が 60 より大きいなら」次の処理を行ラ “成績は B です。”と画面に出力する 2 行目の条件にあった場合の処理内容終了 行目と 12 行目の条件に合わなかった場合、次の処理を行う 、成績は C です。”と画面に出力する 5 行目の条件に合った場合の処理内容終了 List 条件文と論理演算子を使ったプログラム 1 / * 成績を判定するプログラム p セ 2 * / 2 #include く std 土 0. h> 土 n セ kokugo, sansuu; 丐 6 kokugo=8 sansuu=44; 9 迂 ( k0 ugo > 80 & & 8 聞馳 u > 80 ) ( 0 pr 土 ntf ( ”成績は優です。” 11 2 Ⅱ s 馳 80 ) { 13 else if(kokug0>80 p て土 ntf に成績は良です。” 4 15 Ⅱ 8 60 ) { 引 se if(kokug0>60 pr 土 ntf ( ″成績は可です。″ 7 8 else{ 9 2 い p て tf ( ”成績は不可です。” ); 21 “ kokugo ”という変数に 80 というデータを格納する “ sansuu ”という変数に 44 というデータを格納する もし kokugo > 80 かっ sasuu > 80 ならば」次の処理を行う “成績は優です。”と画面に出力する 0 行目の条件に合った場合の処理内容終了 もし kokugo > 80 もしくは sasuu>80 ならば」次の処理を行う “成績は良です。”と画面に出力する 3 行目の条件に合った場合の処理内容終了 もし kokugo > 60 もしくは sasuu>60 ならば」次の処理を行う “成績は可です。”と画面に出力する 6 行目の条件に合った場合の処理内容終了 0 , 13 , 16 行目の条件に合わなかった場合 , 次の処理を行う ー“成績は不可です。”と画面に出力する 9 行目の条件に合った場合の処理内容終了 40 C MAGAZINE 2 1 4

5. 月刊 C MAGAZINE 2001年4月号

TabIe 2 取り出される部分文字列の内容 ・ユーザ INDEX 0 1 2 3 4 文字列の内容 全体 旧アドレス ホスト名 スペース + 工リアス 工リアス れるかもしれません。でも , このケ 場合は , 5 つが正解なのです。 まず基本的に r exec ( ) は , 全体に とで , 正規表現の検索動作を変更できます。 これらの値を論理和で接続することで複数 指定することが可能です。 regexec ( ) の基本動作は , s ⅲ ng が正規表 現パターンにマッチした場合 , 関数の戻り 値として 0 を返すといった簡単なものにな っています (grep のようなものだと思って ください ) 。マッチした部分文字列を取り 出そうとしようとした場合 , 若干ややこし くなります。 regexec() では , 正規表現パ ターン中でグルーピング指定している場 合 , 部分文字列のマッチング位置情報を得 ることができます。 nmatch で指定された個 数だけ , 構造体 regmatch-t が並んだ配列 pmatch にマッチした文字列の位置情報が 格納されます。 regmatch—t は typedef struct regoff—t rm—so ー regoff—t rm—eo ー } regmatch—t ー のような構成になっています。この中の rm so の値が -1 の場合 , マッチしていないこと を意味します。 parse hosts regex. c のー音5 もう 1 度 , parse_hosts—regex. c (List 2 ) を 見てみましよう。この例では regexec() で 5 つの部分文字列を取り出そうとしているこ とがわかると思います。ホストファイルの 書式ではフィールドは最大 3 つなのに , な ぜ 5 つ指定する必要があるのか疑問に思わ ースの , マッ ません。 ーンを記述しておくのもおしゃれかもしれ イルのコメントにマッチする正規表現パタ ルを作るような場合 , そのコンフィグファ のです。また , 何らかのコンフィグファイ 変えることができる強力でクールなものな だけを変更することで , 処理自体を大きく このように正規表現は , パターン文字列 イルではそうもいかないはずです。 か判断できますが , ほかのコンフィグファ 数 inet-aton() の戻り値で妥当な IP アドレス んどうかもしれません。 IP アドレスなら関 ェックルーチンを付けるのは , ちょっとめ いないプログラムに , この IP アドレスのチ 一方 , List1 のような正規表現を使って 思います った IP アドレスを検出しないようになると してみてください。これだけの変更で間違 に変更して , もう 1 度コンパイルと実行を を , ましたが , IP アドレスにマッチする , この間違った IP アドレスにもマッチしてい たか ? parse—hosts-regex. c の実装では , い文字列を入れていることに気がつきまし わざと IP アドレス ( I 内 4 ) としては妥当でな ちなみにホストファイルのサンプルでは , able 2 のようになります。 指定しなければなりません。まとめると T グループを定義しているので , 合計で 5 っ います。その中に " 工リアス " だけのサプ それがある場合のみマッチするようにして リアス " を 1 つのグループとして定義して , ションとして扱われるので , " スペース + 工 になります。もう 1 つは , 工リアスがオプ チした位置情報を返します。これで 3 + 1 で 4 LiIlllX pr 川川 lll ⅲ TIPS ちょっと便利なプログラムを 実装してみよう こまで紹介した regex ( ) を使って , 読む にはちょうどよい長さのプログラムを 2 つ ほど用意しました。 grep を実装してみよう 普段よく使う grep の実装例を示します。 名前は , simple - grep と名付けることにしま す ( 付録 CD-ROM に収録 )。標準入力や複 数ファイルの指定が可能だったり , よく使 われるオプション -c , -n , -1 , -i もサポート しているので , 本物の grep と見分けがつか ないかもしれません ( 冗談です ) 。 読者のみなさんに宿題を出します。 grep に実装されていて , このサンプルプログラ ムに実装されていないオプションの実装を 行ってみてください。たとえば , オプショ ン - q の実装などです。 inetd コンフィグ・ユーティリティを 実装してみよう インターネットスーパーサーバ inetd (ne tkit 版 inetd) の設定ファイルのパースや変更 を行うプログラムの例を紹介します。 Red Hat 系 Linux にあるシステムサービスの設 定ツール chkcon g の名前にちなんで , chk inetcfg と名付けることにします田 st3 ) 。 まず最初に , /etc/inetd. conf の書式を説 明します。 " # " 文字から行末までは , コメ ントになります。基本的に , 次に示すよう な 7 つのフィールドから構成されます。 ・サービス名 ・ソケットタイプ ・プロトコル ・フラグ ・サー / ヾ / ヾス ・サーバ引数 Linux Programming Tips

6. 月刊 C MAGAZINE 2001年4月号

り " から始めます。イベントの発行側と受 理側とをそれぞれクラス TaIker/Listener と 名付け , Talker のメソッド changed() の呼 び出しによって Listener のメソッド update() が起動されるからくりを作ります ( List2 ) 。 これでできあがり , じつに単純です。 試しに使ってみましよう。 Talker から導 出したクラス Counter を用意し , メソッド increment ( ) によって内包するカウント値 を + 1 します。それによって生じるカウント 値の変化を Listener から導出したクラス Di gitprinter に伝え , カウント値をプリント します (List3)0 当然ながら前述の List1 に 示した Document/V1ew とそっくりです。 Step2* 複数の Listener 前述の Step1 では試しにカウント値をプ リントする DigitPrinter を Counter に関連付 けました。同様にカウント値と同じ数の * ' をプリントする StarPrinter を作り , の 2 つ (DigitPrinter/StarPrinter) を 1 つの Counter に関連付けたいとします。・・・・・・現 Talker はイベント通知先である Listener を 1 つしか管理していませんから , これに対応 するには複数個の Listener に対応できるよ う手を加えなければいけません。 こで標準 C + + ライプラリの vector を使 わせてもらいましよう。メソッド setListen er ( ) を addListener ( ) に改め , removeListen er() , removeListeners ( ) を追加します (Lis Fig. 1 List 4 の実行例 1 2 3 4 5 (4)0 実際に動かしてみて Fig. 1 のようにプ リントされましたか ? Step3 、不測の事態に備える ここで List4 にある main() を , c. addListener (s); c. addListener (&d); StarPrinter * s = new StarPrinter; DigitPrinter d; Counter C ー int main ( ) { Counter C ー 土北 main ( ) { あるいは , re turn の C. increment ( ) ー delete 町 C. increment ( ) ー return の c. increment ( / / ※ C. increment ( ) ー c. addListener(&s); StarPrinter s ー コンパイル時には何のエラーも出ません のいずれかに変更してみます。 c. addListener (&d); DigitPrinter d ー いのではないでしようか。 してくれたほうがカッコイイというか美し 誰にも迷惑をかけないよう自分の痕跡を消 のですが , 自分 (Listener) が死ぬときには moveListener(s) しなかったのがいけない からです。もちろん delete に先立って c. re インスタンスのメソッドを呼び出している ているというのに , Counter は存在しない s が delete / スコープから外れていなくなっ とが起こるでしようね。 StarPrinter である が , 実行時におそらく※の地点でヤノヾいこ し彎々 オフジェクト工房 ・ Listene 「クラスを安全にする そこで Listener のコンストラクタ / デスト ラクタにひねりを加えます。どうやるかと いうと , 世の中 ( プログラム中 ) で生きてい る ( 存在している ) 全 Listener を管理する集 合すなわち。戸籍 ' set く Listener * > を Listener の sta ⅱ c メンバとして用意します。そして Li stener のコンストラクタで出生届 ( 戸籍に 追加 ) , デストラクタで死亡届 ( 戸籍から抹 消 ) を出しておくんです。そして , TaIker:: update() で各 Listener を起動するに先立っ てその Listener が、戸籍 ' に登録されている かを調べます (List 5 ) 。こうすればプログ ラマが明示的に Talker::removeListener ( ) を呼び出さなくても Listener のデストラク タがやってくれたことになるわけです。 ただし , この解決策には若干の問題があ ります。つまり Talker::update() のたびに 戸籍 ' の確認を行わなくてはならないため , パフォーマンスが少し落ちてしまうでしよ う。 Listener の総数がそれほど多くなけれ ば速度劣化はほとんどないでしようが , 大 量の Listener を必要とするシチュエーショ ンでは気になります。・戸籍 ' として使って いる set の検索に要する時間計算量は , set の要素数 N の ( 2 を底とする ) 対数に比例し ますから。戸籍 ' への登録は必要最小限に抑 えたほうがいいはずです。そのため , List 5 では Listener のコンストラクト時に登録 していますが , これを改めて TaIker::addLi stener ( ) 呼び出し時にやったほうが。ほん の少しですが be れ er でしよう。そうすれば " 生きてはいるけど T ker に関連付けられて いない "Listener は・戸籍 ' に登録されていな いので , 検索時間がそれだけ向上するはず です。とはいっても微々たるモノでしよう けどね ^ ^ ; 。 それでも 4 秒オーダのパフォーマンスの 低下でも我慢がならないならどうするのか Listener の各インスタンスに addLis tener() した Talker の集合を持たせ , デス トラクタの中でこの集合にある各 Talker に どびてククのオブジェクト工房 81

7. 月刊 C MAGAZINE 2001年4月号

主人公のキャラクタでは , ちょっとぐら いかすった程度では当たったことにはなら ないようにしたり , 敵の場合は多少当たり 判定の範囲を広くとると弾が当たりやすく なります。こうしておくとゲームの難易度 をプレイヤにやさしいほうにちょっと下げ ることができます。 WonderWitch で表示する絵は 8X8 ドット という単位になってしまうので , それらを 考えて , 当たり判定の範囲の大きさをキャ ラクタの種類によって変えるようにしてお 1 / * その方向へ移動するよう自機の座標を加減する * / / * 方向ボタンが押されていたら , / * 用意をする / * あるボタンが押されていたら弾を撃ち出す / * ボタン判定 * / void gamelnput(void) / * プレイヤからの入力処理 * / / * VSYNC 待ち / * 自機、敵機のキャラクタの移動 / * 背景の組み立てとスクロール void gameDraw(void) / * 1 回ぶんの画面を組み立てて表示 * / シューティングゲームの骨格 LIST きます。 / * ループ本体 * / void gameLoop ( void ) / * ループ本体 * / flagyarareta を True に / * 敵機が何かにぶつかったら , 敵機の flagyarareta を True に / * 自機が何かにぶつかったら、自機の / * 当たり判定 * / void gameExecute(void) / * ゲーム処理 * / ーする * / ーする * / / * 1 回ぶんの画面を組み立てて表示 * / do { / * このループが 1 回動くことでゲームがひとつ進む * / ) 曲 i 厄い gameCheckEnd( ) / * ゲームを終了させるかどうか * / gameExecute( / * ゲーム処理本体 * / gamelnput( / * プレイヤからの入力処理 * / gameDraw( ■重なったあとの処理 機の弾にぶつかった」 , 「自機が敵機にぶつ する処理」を呼び出します。逆に「自機が敵 う条件だったら「敵機に応じた得点を加算 もし「敵機が自機の弾にぶつかった」とい れる変数から判断します。 前述したキャラクタの構造体データに含ま どの種類のキャラクタがぶつかったかは , ものヘ反映させることをします。 次はそれぞれに場面に応じて , ゲームその キャラクタ同士が重なったのであれば , 遊びのアヒ かった」などゲーム終了の条件となるもの であれば , そのための処理を呼び出します。 たいがいすぐには終わらず , 爆発シーンな どを表示してからゲーム終了となるはずで す。そこで「ゲーム全体の状態」を判断でき るフラグ用変数を作り , その変数へ「ゲー ム終了作業中」という意味の値を入れてお きます。表示が終わるのを待ったあとに のフラグを確認して , ゲーム終了の処理を プログラムの全貎 呼び出すようにします。 通信対戦ケープルが必要になりますが , 携 です。 WonderWitch のカートリッジ 2 っと 次回は通信対戦について取り上げる予定 b サイトで公開する予定です。 プログラムについては C MAGAZINE の We となれば幸いです。今回作成したサンプル 処理をしています。本稿がその理解の一助 の単純なゲームシステムとは裏腹に複雑な このように , シューティングゲームはそ 終わりに ます。 組み合わせることでゲームを実現していき これ以外はここまで解説してきた手法を 機や敵機を登録するようにしていきます。 めたバッフアを用意し , そのバッフアへ自 キャラクタは「今表示している」ものを集 などを行います。 中で「画面を組み立てて描画」 , 「入力処理」 meLo 叩 () にあるループの部分です。この フレーム 1 回ぶんの作業を行うのは , ga きます。 ます。ここへ解説してきた処理を加えてい ードの基本的な構造は List 1 のようになり ログラムにするときがきました。ソースコ こまで紹介してきた方法をまとめてプ 遊びのレシピ fo 「 WonderWitch ぞお楽しみに 帯ゲーム機らしい遊び方ができます。どう 1 35

8. 月刊 C MAGAZINE 2001年4月号

/ * 重なっている * / という式で得られます。 ( & 1 ) というのが当 たり判定で使われる範囲となります。キャ ラクタの大きさによってこの部分を変える ようにします。 ほかにもいろいろなプログラミングの方 法があります。専用の 2 次元配列を用いた り , 重なり具合を矩形ではなく円などとし WonderWitch でのデバッグは , 何かとめん どうなところがあります。ソースコードレベ ルのテパッグはできませんし , WonderWitch へいったんプログラムを転送したうえでプロ グラムを実行させるというわずわらしい手順 が必要になります。また , 致命的なミスがあ ると表示などがすぐにうまくいかなくなり , テパッグ作業がスムーズに行えません。 セオリーだとは思いますが , デバッグに関 する筆者の私見は , 「バグがあるかどうか検 証して動作を確認した部分を積み重ねていき , 問題があればその一歩手前の状態に戻す」と いうことを繰り返しているだけというもので す。「何々をしている処理」 , 「何々から受け取 る処理」というように部分ごとに分けてプロ グラミングをしている場合 , その部分でバグ がないことがわかっているなら , ほかの部分 に問題が必ずあるとすぐにわかります。この ようにバグがない部分を増やしていきながら , バグがあると思われる範囲を狭めていくよう にプログラミングを行います。 これを Wonde 「 Witch で実践するには , 筆者 の場合 , その方法の 1 っとして , ・始めに仕組みやアルゴリズムを実装する ・そのあとで Wonde 襯 itch 特有の処理を加 ・ WonderWitch 特有の処理は , それぞれの 処理が自分で使いたい形にできるかどう かテスト用ソースコードを作る というようにしています。 て計算で確認する方法などがよく使われて います ただ , この方法のままでは現在表示され ているキャラクタすべてに対して調べなけ ればいけません。そのため配列やハッシュ などを利用してできるだけ計算量を減らす 方法が使われています。 今回の当たり判定については , X 軸方向 にキャラクタがいるかどうかを示すバッフ アを作り , まずここでいったん調べてから 前述の式で当たったかどうかを判定するよ デバッグ方法 もっともバグが入りやすいのは , 自分で作 ったアルゴリズムや関数の連係など「ゲーム そのものに関係する処理や仕組み」です。シ ューティングゲームでは , 敵の動きを実現す るところや進行管理などがこれにあたります。 アドベンチャーゲームではシナリオテータを 解釈する部分などです。こうしたところは , Wonde 「 Witch で実際の画面表示などを行わな くてもテストできる場合がほとんどのはずで す。 それなら WonderWitch の処理を使わないよ うにしていったんソースコードをまとめ , そ れらをテストしてから WonderWitch の機能を 使うという流れでプログラミングするように してみましよう。こうして WonderWitch の機 能を使っていないソースコードにしておくし 通常の統合開発環境を使ったソースコードレ ベルでのデバッグ作業が行えます。 筆者は Borland C + + Builder 5 を愛用してい るので , こうしたアルゴリズムや処理の流れ をまとめたら , Borland C + + Builder 5 上でソ ースコードのトレースなどのデバッグ作業を 行い , ある程度処理の流れを完成させていま す。そのあとで表示など WonderWitch で提供 される機能を加えるようにしています。変数 値なども別ウインドウで確認することができ , ソースコードを 1 行ずつ解釈して実行するス テップ / トレース実行の機能によってプログラ ムの処理の流れが直感的に追いやすくなるの で , 非常に気分よくデバッグできます。 コラム 2 うにしています。この当たり判定は「キャ ラクタが移動したとき」に行うようにしま す。 ■当たり判定の注意点 スプライトとして設定したときに周囲が 空いているキャラクタ同士がぶつかった場 合 , その状態によっては「ぶつかったよう には見えない」場合が出てきます。このと きはそのぶんも含めて当たり判定を考えな ければいけません。 もちろん , ほかの開発環境でもかまいませ ん。フリーのテパッガとしてはたとえばボ ーランド ( 株 ) が無償配布している TurboDebu gger などがあります。このようにしてある程 度仕組みなどを完成させてからプログラミン グを進めるとよいでしよう。 Wonde 「 W 瓰 h の各機能は魅力的ですが , そ れが自分の思ったとおりの処理をしてくれる かどうかはわかりません。実際にそれらをテ ストする簡単なプログラムを作って試すこと が必要になるはずです。このとき , ただ単に テストをするだけではもったいないものです。 作っている本当のプログラムにその機能を組 み込むことを考えて , 呼び出す順番や関数な どを整えておくようにします。そうしておく し前述の仕組みを作ったソースコードとこ のテストプログラムから取り出した関数など を組み合わせることが簡単にでき , それによ ってバグがそれほど含まれないソースコード を作り出すことができます。 とはいえ , ある程度完成したプログラムを WonderWitch 上で微調整するときは , 昔なが らの変数など直接表示して行う p ⅱ ntf デバッグ (sprintf + text_put_string デバッグ ? ) をしてい ます。このとき表示が乱れることがあるので , 画面上の特定の部分はデバッグ用として使う ように分けることをしています。 このようにいろいろと気をつかっていけば スムーズなデバッグ作業は可能になるもので す。がんばってみてください。 1 3 イ C MAGAZINE 2 1 4

9. 月刊 C MAGAZINE 2001年4月号

00 フログラミング入門 を見てみましよう。日本人にはこの言葉は ればいいわけです。 上記の ER 手法や , そのほか多くのプログラ なじみがなく , 聞いたときにイメージの湧 ミング開発手法が「オプジェクト指向」の中 シミュレーションと こは 1 つ「お任せ手 に流れ込んでいます。わかりにくさはその きようがありません。 オブジェクト指向 法」といってみてはどうでしようか。「お任 せいでもあります。 せ手法プログラミング」「お任せ手法分析・ 現在のオプジェクト指向は全体としてか 設計」。威厳はないですが , 親しみやすい オプジェクト指向はシミュレーションの なり複雑であり , 専門用語を使ってしか説 分野から生まれました。考えてみれば , シ と思います。 明できない面もあります。「継承」や「クラ ただ , これは本稿だけの言葉なので , 外 ミュレーションこそ「お任せ手法」そのもの ス」がないオプジェクト指向もありえます です。全体として制御せずに , 要素間の作 部の人に言うときには注意してください。 が , しかし自動車にヘッドライトやノヾンパ 取引先に「当社ではオプジェクト指向を活 用に任せているのですから。さらに , コン ーがなかったらちょっと困るというように 用してプログラム開発を行っております」 ピュータで行う仕事の多くの部分は , シミ 根元的に必須ではないにしても無視するこ ュレーション的な性質を持ちます。たとえ といって「おお , よくわからないけど難解 とはできません。 で高級そうだ。人月単価を上げてあげよう」 ば在庫管理システムなんかも , 現実の在庫 いわゆる「オプジェクト指向」については となるところが , 「当社ではお任せ手法を をコンビュータ内に写像し , その動きをな さまざまな本が出版されているので , 詳し 活用してプログラム開発を行っております」 ぞるものなので , 多少強引ですが , シミュ いことはそちらを見てください。ただその といって「ううむ。なんだかいいかげんで レーションに似たものといえます。 根底に , 「任せること」と「シミュレーショ 頼りなさそうだな。契約を破棄しよう」と オプジェクト指向は現在でもシミュレー ン」を仮定すると全体像がとらえやすく , ま ションの概念を引きずっています。ただ なっても責任は持てません。 た理解しやすくなると思います。 このシミュレーションというパラダイム ( 考 だけど「オプジェクト指向はお任せ手法 最近「エージェント指向」という言葉を聞 である」と考えるといくらかご利益があり え方の枠組 ) が何にでも適用できると思う くようになってきました。意味が混乱して ます。オプジェクト指向プログラム言語 , ところにもう 1 つの誤解があると思うので いる面もありますが , これもオプジェクト たとえば Java などを使って開発していると , すが , それはここでは触れないでおきます。 に判断力を持たせたような「エージェント」 Java の場合メッセージは public オプジェクト指向はプログラミングだけ というモジュールに機能を任せるという理 メソッド なメソッドとして実装されます一一 - で , 戻 にとどまらず , ER モデル分析 ( システムを 解でいいでしよう。オプジェクト指向と同 り値がないものをよく使います。これはオ 「実体」と「関連」ととらえて分析する手法 ) じレベルにおいて対立するものではないと の影響を受け , 「オプジェクト指向分析」と プジェクト指向としては当然のことですが 思います。 機能分割による構造化手法に慣れた人に いう分野が発展しました。簡単にいうと , 現実世界をオプジェクトに置き換えて分析 は , 気持ち悪く感じるはずです。メソッド する , つまり現実世界をオプジェクトによ とかメッセージとかいっても , 形態として は C 言語の関数と同じなので , それを連想 ってシミュレートする手法です。これは今 ではプログラムの分野だけでなく , 企業・ してしまいます。 C 言語の標準関数では返 コンピュータなどをいじる人は , どちら り値がないのはむしろ例外であり , printf() 組織・社会構造の分析にも適用しようとい かといえば神経質で , 細かいことまで自分 関数なんかでもほとんど使われないのに返 う動きが出ています。 でやりたがる性格だと思います。統計をと り値として出力文字数を返してきます。メ ったわけではありませんが・・・ 現在の ソッドも , 返り値として , せめて実行の状 先に , 「度量を大きく持って」と書きまし オブジェクト指向 況を報告しないとまずいのではないか , な た。オプジェクトに任せるということは , し どと思ってしまいます。 ばしばこの性質が要求されます。ある機能 しかし「お任せ手法」と考えれば悩まなく しかし , 「オプジェクト指向」が単なるお について「自分で制御したい」という欲求は , て済みます。任せた以上は細かいことは考 任せ手法であるとか , シミュレーション用 誰でも持つものだと思います。しかしそれ えず , 度量を大きく持ってオプジェクトに の手法だと考えても , それですべてを表し をやってはなりません。オプジェクトに任 任せます。もし向こうでどうにもならない きれるものではありません。オプジェクト せたのだから , 任せた以上は信じるという ような非常事態が起きたら , 「例外」という 指向は多くの人が洗練させてきたもので , 態度が肝心です。 SOS を投げてくるから , そのとき対処をす いろいろ特別な意味が付与されています。 会社組織にたとえてみましよう。資料の がまんがだいじ 58 C MAGAZINE 2 1 4

10. 月刊 C MAGAZINE 2001年4月号

理者が自由に記述できるものではなく , 送 り手と受け手の存在するデータベースの表 現がその目的であるため , 確定していない 仕様を前提としてプログラミングすること には問題もある。 そのあたりを念頭に置き , 情報を仕入れ ていく必要がある。 XML に関しては , ま だ実践的な技術を解説した書籍も少ない。 多くの書籍は概要や小手先の技術紹介にと どまっている。これは致し方ないだろう。 ソフトウェアの世界は , 先生だって頼り にならないのだ ( 政治業界の先生方もあま り頼りにはならないようだが ) 。いろいろ な「偉い人」がいろいろなことを言っている。 誰の , どの言葉を信じるかは , 結局あなた の考え方しだいなのだ 学校を見ればわかる。先生の言っている ことが 100 % 正しかったのは , もう遠い昔 のことなのだ [ 注 8 ] 1963 年 11 月 22 日 ( 日本時間 , 同 23 日 ) 。 TBS ( 東京放送 ) と米 CBS が協力し , 通信衛星を使っ た初の日米テレビ中継で , テキサス州ダラスの大 統領パレードを放映した。 [ 注 9 ] 「チーズはどこへ消えた ? 」 ( スペンサー・ジ ョンソン著 / 扶桑社刊 ) 。すでに 90 万部売れ , 100 万部目前だそうだ。たしかに「わかりやすい本」 だとは思うけどねえ・・ しかし変わらぬものもある 食べれば減ってしまうチーズ。過去にし がみつくなというのは , 一面で真実だ。し かし , チーズは 1 日では作れない。時を経 てなお , 変わらない価値もまたある。 時代は論理から感覚へ かって , プログラミングはテキストエデ イタによるソースコードの入力と , コマン ドラインによるコンパイラの起動という , 実に「じみー」な作業だった。 今でも , そのようなアプローチをとるこ とはできる。ただ , VisuaI Basic( 以下 VB) などの RAD に代表されるビジュアルプロ グラミングが主流となってからは , そうい / 0 c MAGMINE 2001 4 った手間のかかる作業が敬遠されるように なったのだ。 UNIX でさえ , XWindow 上で はビジュアルプログラミングが主流になり つつある。 かって , 「パソコン通信やインターネッ トの普及によって , 文章によるコミュニケ ーションが復活する。若者の文字離れは杞 憂にすぎない」といった学者がいた。が その予想はものの見事に外れた。 今や , ケータイで交わされるメールはす べてといってよいほど「話し言葉」だ。顔文 字も盛んである ( ^ ^ ; ) ゞ。話し言葉のエン ジンは抽象的な「文字」ではなく直感的な 「印象」だということに注意していただきた い。ネットの普及によって , 論理的な思考 はますます日陰者の立場に追いやられてい 仕組みを知らなくても先生になれる ? ! プログラミングは , できるだけ楽なほう がいい。ユーザインタフェイスを視覚的に 構成できることで , 仕様変更や改良も容易 になった。開発サイクルが速くなった現在 では , ちまちまとソースコードを読み解く 時間ももったいないのだ 効率化の面で , ビジュアルプログラミン グは多大な貢献をした。しかしその半面 , プログラミングの学習にとって非常に重要 な「システムの理解」が省略されるようにも なった。ここでいうシステムとは , ハード ウェアと OS のアーキテクチャである。画 面の組み立ても動作の定義も感覚的に行え るため , 実際のところどのような仕組みで プログラムが動作しているのか , わからな いままにプログラミングできてしまう。 以前筆者は , メールマガジンで VB のプ ログラミングテクニックを解説している人 から問い合わせを受けたことがあった。話 を聞いてみると , その人は Windows のメ ッセージループやリソース ID はもちろん , API と DLL の関係も , イベント駆動の意味 すらも理解していなかった。それでも , 「作業手順としてのプログラミングを教え る」ことは可能なのだ。 「優れたドライバー = 優れたエンジニア」 とは限らない プログラムを作るだけなら , あまり深い 部分を知っておく必要はないだろう。車の メカニズムを知らなくても , 運転はできる。 目的地にたどり着ければよいだけならそれ で十分だ。しかし , 「優れたドライバー 優れたエンジニア」であるとは限らない。 コンピュータ技術では時として大きなパ ラダイムシフトが訪れる。手続き型プログ ラミングからオプジェクト指向への転換も そうだったし , 現在の Web プログラミン グもそうだろう。 Java や ASP の VB Script を見ればわかるよ うに , これまでマウスでドラッグ & ドロッ プしていた作法が , また旧来の記述式に戻 ってしまった。 . NET の登場でビジュアル 化が進むだろうが , Web プログラミング に絞れば , これまでのビジュアルプログラ ミングとは異なるアプローチが必要になる だろう。 そういったとき , ハードウェアや OS に ついての理解があれば , 新しい技術やその 基となる思想を理解するのは比較的容易 だ。表層が変わるだけで , 根底部分は変化 していないからだ 文字によって定着された思考の過程は , それを読み解くことによって ( ほば ) 正しく 受け継がれていく。一方 , 画像による印象 は個々人の解釈であり , 語り継げば変質す る。プログラミングは , ある意味で確かに アートつばいが , アートそのものではない。 空飛ぶ自動車のメカニズム IBM と GM が , 「 5 年後には空飛ぶ自動車 を作る」という合意書を交わしたらしい。 これまで地上を走る自動車を漫然と運転し ていた人にとって , 空飛ぶ自動車はまった く新しい乗り物に映るだろう。 しかし , 自動車の基本的なメカニズムを