<?xml version="1.0" encoding="UTF-8" ?> <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>ゲームプログラミング</title><link>http://blogs.wankuma.com/nagise/category/1411.aspx</link><description>ゲームのプログラムに関するetc.</description><managingEditor>凪瀬</managingEditor><dc:language>ja-JP</dc:language><generator>.Text Version 0.95.2004.102</generator><item><dc:creator>凪瀬</dc:creator><title>HEXのマインスイーパを作ってみた</title><link>http://blogs.wankuma.com/nagise/archive/2008/06/11/142781.aspx</link><pubDate>Wed, 11 Jun 2008 09:22:00 GMT</pubDate><guid>http://blogs.wankuma.com/nagise/archive/2008/06/11/142781.aspx</guid><wfw:comment>http://blogs.wankuma.com/nagise/comments/142781.aspx</wfw:comment><comments>http://blogs.wankuma.com/nagise/archive/2008/06/11/142781.aspx#Feedback</comments><slash:comments>11</slash:comments><wfw:commentRss>http://blogs.wankuma.com/nagise/comments/commentRss/142781.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/nagise/services/trackbacks/142781.aspx</trackback:ping><description>&lt;p&gt;&lt;a href="http://blogs.wankuma.com/gshell/archive/2008/06/09/RemindMineSweeperMakingLectureOnMS.aspx"&gt;ふと触発されて&lt;/a&gt;マインスイーパっぽいものを作ってみた(スター●ーカーとも言う)。後悔はしていない。&lt;/p&gt;

&lt;img src="http://nagise.wankuma.com/image/hex_minesweeper.jpg"&gt;

&lt;p&gt;JavaWebStartでの起動は&lt;a href="http://nagise.wankuma.com/jws/hex_minesweeper.jnlp"&gt;こちら&lt;/a&gt;から(約1.7MByte)。&lt;/p&gt;

&lt;p&gt;ご褒美グラフィックは&lt;a href="http://blogs.wankuma.com/ryoichi/"&gt;りょーいち&lt;/a&gt;さんとこからシュウたんの画像を借りてきました。事後報告でごめんなさい。&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/nagise/aggbug/142781.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>凪瀬</dc:creator><title>HEXなリバーシを作ったよ</title><link>http://blogs.wankuma.com/nagise/archive/2008/05/25/139257.aspx</link><pubDate>Sun, 25 May 2008 02:34:00 GMT</pubDate><guid>http://blogs.wankuma.com/nagise/archive/2008/05/25/139257.aspx</guid><wfw:comment>http://blogs.wankuma.com/nagise/comments/139257.aspx</wfw:comment><comments>http://blogs.wankuma.com/nagise/archive/2008/05/25/139257.aspx#Feedback</comments><slash:comments>10</slash:comments><wfw:commentRss>http://blogs.wankuma.com/nagise/comments/commentRss/139257.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/nagise/services/trackbacks/139257.aspx</trackback:ping><description>&lt;p&gt;&lt;a href="http://blogs.wankuma.com/gshell/archive/2008/05/23/ProposalOfReversiCompetition2.aspx"&gt;
リバーシ ゲームＡＩ 続報&lt;/a&gt;でネタに上がっていたHEX(ヘックス。六角形による盤面のこと)でのリバーシ。&lt;/p&gt;

&lt;p&gt;ちょうど手元に自作のHEX用フレームワークがあったので、リバーシを乗せてみました。
AIは一番多く取れるところに置くだけのお馬鹿さんです。&lt;/p&gt;

&lt;img src="http://nagise.wankuma.com/image/hex_reversi.png"&gt;

&lt;p&gt;JavaWebStartでのデモは&lt;a href="http://nagise.wankuma.com/jws/hex_reversi.jnlp"&gt;こちら&lt;/a&gt;。&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/nagise/aggbug/139257.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>凪瀬</dc:creator><title>タスクシステムにコルーチンを組み込むには</title><link>http://blogs.wankuma.com/nagise/archive/2008/03/12/127443.aspx</link><pubDate>Wed, 12 Mar 2008 23:31:00 GMT</pubDate><guid>http://blogs.wankuma.com/nagise/archive/2008/03/12/127443.aspx</guid><wfw:comment>http://blogs.wankuma.com/nagise/comments/127443.aspx</wfw:comment><comments>http://blogs.wankuma.com/nagise/archive/2008/03/12/127443.aspx#Feedback</comments><slash:comments>1111</slash:comments><wfw:commentRss>http://blogs.wankuma.com/nagise/comments/commentRss/127443.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/nagise/services/trackbacks/127443.aspx</trackback:ping><description>&lt;p&gt;&lt;a href="http://blogs.wankuma.com/nagise/archive/2008/03/11/127284.aspx"&gt;型を継承する以上はis-aであるべき&lt;/a&gt;
ではコルーチンを使おうが何だろうが、java.lang.Iterable(C#ならSystem.Collections.Generic.IEnumerator)であるからには
その型としての機能性を全うすべきだと主張しました。&lt;/p&gt;

&lt;p&gt;では、本題のタスクシステムでコルーチンを扱うケースでどのように設計するべきかを考えてみましょう。&lt;/p&gt;

&lt;h4&gt;そもそもタスクシステムって？&lt;/h4&gt;

&lt;p&gt;ゲームにオブジェクト指向の考えを部分的に取り入れたのがタスクシステムと言えるでしょう。
タスクシステムでやっていることは、&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
&lt;li&gt;キャラクタを表現する構造体を作る
&lt;li&gt;その構造体には関数ポインタによって挙動の違いを表現できる機能性を持たせる
&lt;li&gt;構造体は双方向リストなどの構造で複数のデータを管理できるようにする
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;といったところです。
これは、非オブジェクト指向であるC言語では「システム」と称するだけのものかもしれませんが、
JavaやC#といったオブジェクト指向を前提としたモダンな言語ではごく日常的な表現にすぎません。&lt;/p&gt;

&lt;p&gt;キャラクタを表現するのは抽象クラスかinterfaceを用います。&lt;br&gt;
関数ポインタによる挙動の違いは、抽象クラスもしくはinterfaceの実装クラスのポリモフィズムによって
行うことができますから、わざわざ関数ポインタなどを用いる必要はありません。&lt;br&gt;
双方向リストにするためにクラスに前後へのポインタを持たせることもできますが、
現在のプログラミング言語であれば外部のコレクションAPIを用いる方が自然でしょう。&lt;/p&gt;

&lt;p&gt;このように、C言語で作られるタスクシステムがオブジェクト指向言語と極めて相性が良いのは、
タスクシステム自体がオブジェクト指向の考え方を取り入れたシステムだからに他なりません。&lt;/p&gt;

&lt;p&gt;C#で大雑把なイメージを表現すると、&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
// ゲーム中のキャラクタを表現するインターフェース&lt;br&gt;
interface GameCharacter&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;// 1フレーム分の処理を行う&lt;br&gt;
&amp;nbsp;&amp;nbsp;public GameCharacterStatus update();&lt;br&gt;
&amp;nbsp;&amp;nbsp;// キャラクタの描画&lt;br&gt;
&amp;nbsp;&amp;nbsp;public void draw();&lt;br&gt;
}&lt;br&gt;
&lt;br&gt;
class TaskSystem&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;private List&amp;lt;GameCharacter&amp;gt; list;&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;public void GameMainLoop()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;while (true)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;foreach(GameCharacter gchar in list)&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GameCharacterStatus status = gchar.update;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// キャラクタのステータスによって削除などを行う&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// 描画処理&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;gchar.draw();&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
}
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;この例では、敵が倒されて除去されるような部分はタスクシステム側の責務としています。
そのため、キャラクタが返すステータスを見て、倒されたなどの状態をみてタスクシステムのListから
除去するように実装する必要があります。&lt;/p&gt;

&lt;h4&gt;キャラクタの制御にコルーチンを用いたい場合は？&lt;/h4&gt;

&lt;p&gt;このサンプルでは、1フレームの処理を行うためにupdate()というメソッドを用いました。
この&lt;strong&gt;内部がどのような実装になっていようともタスクシステム側は関知しません&lt;/strong&gt;。
ただ、1フレーム分の処理さえしてくれればいいのです。
ここがオブジェクト指向的な抽象ですね。&lt;/p&gt;

&lt;p&gt;C#のコルーチンはIEnumeratorで表現されます。
「繰り返し値を返すもの」として扱われるわけですね。
状態を表すオブジェクト(ここではGameCharacterStatus)を返すコルーチンを用意した場合、&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
class HogeCharacter : GameCharacter&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;// コルーチンを保持するメンバ&lt;br&gt;
&amp;nbsp;&amp;nbsp;private IEnumerator&amp;lt;GameCharacterStatus&amp;gt; coroutine;&lt;br&gt;
&amp;nbsp;&amp;nbsp;public HogeCharacter()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// コルーチンの初期化&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.coroutine = this.getCoroutine();&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;// 1フレームを処理して状態を返す&lt;br&gt;
&amp;nbsp;&amp;nbsp;public override GameCharacterStatus update()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.coroutine.MoveNext();&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return this.coroutine.Current;&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;// コルーチンの実装&lt;br&gt;
&amp;nbsp;&amp;nbsp;private IEnumerator&amp;lt;GameCharacterStatus&amp;gt; getCoroutine()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GameCharacterStatus status = new GameCharacterStatus();&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield return sutatus;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield return sutatus;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
}
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;といった感じになると思います。
このように、コルーチンの実体と外界との接点をyield returnの値のみにすることで
スパゲッティコード化することを防いでいるわけです。&lt;/p&gt;

&lt;p&gt;こういった工夫は小さい規模のプログラムではメリットを感じにくいところですが、
大規模化するほど効果を発揮します。&lt;/p&gt;

&lt;h4&gt;状態を表すオブジェクトなんて作ってられないというのであれば&lt;/h4&gt;

&lt;p&gt;もし、こうしたように状態を表すオブジェクトを返すという作りにしにくい場合はどうでしょうか？&lt;br&gt;
状態を表すクラスを宣言するよりも複数の値を返したい場合など、メンバ関数を用いてやり取りする方が楽な場合もあります。
私は二つの値を返したければ、それを保持するクラスを作ることを厭わない人間ですが、
いろいろな人のソースを見ていると、クラスや構造体をわざわざ宣言するということがためらわれるという意見も多いようです。&lt;/p&gt;

&lt;p&gt;C#ではジェネリクス型パラメータとしてVoidを設定することはできないようなので
値を返さないIEnumeratorにする場合はダミーの値を返すようにする必要がありますね。&lt;br&gt;
こういったケースでは
&lt;a href="http://blogs.wankuma.com/myugaru/archive/2008/03/10/127092.aspx"&gt;C#のyieldに対する誤解と私の見解&lt;/a&gt;
で主張されているように「コルーチンとしてのyieldは無意味な値を返すべき」というのは当たっていると思います。&lt;br&gt;
ただし、それはC#のジェネリクス型がVoidを表現できないことからくる実装上の工夫という泥臭い理由によることも忘れてはなりません。&lt;/p&gt;

&lt;p&gt;ともあれ、そのような場合はメンバ変数などを用いて状態をやりとりすることになると思いますが、
そのような実装の話は、あくまでGameCharacterというインターフェースの実装の中に隠蔽される事項です。
これはオブジェクト指向で言われる&lt;strong&gt;カプセル化の概念&lt;/strong&gt;ですね。&lt;/p&gt;

&lt;p&gt;&lt;code&gt;
class PiyoCharacter : GameCharacter&lt;br&gt;
{&lt;br&gt;
&amp;nbsp;&amp;nbsp;// コルーチンを保持するメンバ&lt;br&gt;
&amp;nbsp;&amp;nbsp;private IEnumerator&amp;lt;GameCharacterStatus&amp;gt; coroutine;&lt;br&gt;
&amp;nbsp;&amp;nbsp;public PiyoCharacter()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// コルーチンの初期化&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.coroutine = this.getCoroutine();&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;// 1フレームを処理して状態を返す&lt;br&gt;
&amp;nbsp;&amp;nbsp;public override GameCharacterStatus update()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;this.coroutine.MoveNext();&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// メンバ変数を通してstatusオブジェクトを構築する&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;GameCharacterStatus status = new GameCharacterStatus();&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return staus;&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
&amp;nbsp;&amp;nbsp;// コルーチンの実装&lt;br&gt;
&amp;nbsp;&amp;nbsp;private IEnumerator&amp;lt;int&amp;gt; getCoroutine()&lt;br&gt;
&amp;nbsp;&amp;nbsp;{&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield return 0;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield return 0;&lt;br&gt;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;// ...&lt;br&gt;
&amp;nbsp;&amp;nbsp;}&lt;br&gt;
}
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;このような場合、PiyoCharacterクラス内部とコルーチン部分とではメンバ変数によるデータのやり取りが行われます。
カプセル化という観点ではあまり望ましくない状態ですが、このPiyoCharacterクラスの内部だけの話として
隠蔽してしまうことでスパゲッティコード化することを防いでいます。&lt;/p&gt;

&lt;p&gt;いずれにせよ、外側(ここではTaskSystemクラス)から見た場合は、
GameCharacterインターフェースに対するポリモフィズムであり、
内部の諸事情は一切考慮する必要がありません。&lt;/p&gt;

&lt;p&gt;この考慮する必要がないという部分がオブジェクト指向で言われる隠蔽であり、
また再利用を高めるための抽象化でもあるのです。&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/nagise/aggbug/127443.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>凪瀬</dc:creator><title>ゲームのバグにみるアルゴリズム - バッファオーバーフロー</title><link>http://blogs.wankuma.com/nagise/archive/2007/10/09/100735.aspx</link><pubDate>Tue, 09 Oct 2007 20:28:00 GMT</pubDate><guid>http://blogs.wankuma.com/nagise/archive/2007/10/09/100735.aspx</guid><wfw:comment>http://blogs.wankuma.com/nagise/comments/100735.aspx</wfw:comment><comments>http://blogs.wankuma.com/nagise/archive/2007/10/09/100735.aspx#Feedback</comments><slash:comments>1946</slash:comments><wfw:commentRss>http://blogs.wankuma.com/nagise/comments/commentRss/100735.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/nagise/services/trackbacks/100735.aspx</trackback:ping><description>&lt;p&gt;Programming SHOT BARへようこそ。&lt;br&gt;
ゲームのバグシリーズはひそかな人気シリーズです。ぼちぼち検索エンジンから飛んでくる方がいらっしゃいますね。
今回はダンジョンズ＆ドラゴンズ シャドーオーバー・ザ・ミスタラ(1996年カプコン。略称で以下D&amp;D2)です。&lt;/p&gt;

&lt;h4&gt;ジャーレッドのアイテム増殖バグ&lt;/h4&gt;

&lt;p&gt;このD&amp;D2ではゲーム開始してしばらく進むとプレイヤーの名前を入力する画面があります。
名前入力は最大6文字で、入力した名前によってA～Jの10タイプに振り分けられ、
ステータスや初期装備アイテムが変化するというシステムになっています。
プレイヤーの選択したキャラクタの職業別にデフォルトの名前があるのですが、
2プレイヤー側の戦士の「ジャーレッド」という名前のみ6文字のデフォルト名となっており、
この名前を使った際にはバッファオーバーフローが発生します。&lt;/p&gt;

&lt;p&gt;つまり、文字入力の際に6文字目だったらカーソル位置を変更しないことで、
7文字目以降の入力が行われないように制約しているのですが、
デフォルトネームを選択した場合に漏れがあり、「ジャーレッド」を選択した時点で「決定」せずに
続けて入力するとカーソル位置が7文字目に移動してしまい、
名前のメモリの後ろの部分にデータ入力することが出来てしまうのです。&lt;/p&gt;

&lt;p&gt;このバグにより、所有アイテムや装備品などのステータスを自由にいじることが出来ます。
といっても、メモリに直接値を書き込むのですから、メモリ構造が把握できていないと思ったように
装備品やアイテムを変更することは出来ませんが…。&lt;/p&gt;

&lt;p&gt;なお、不用意にメモリを書き換えるとゲームがフリーズしてしまいます。
ゲームバランスを激しく損ないますし、迷惑ですからお店では試さないでくださいね。&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/nagise/aggbug/100735.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>凪瀬</dc:creator><title>ゲームのバグにみるアルゴリズム - GameOver</title><link>http://blogs.wankuma.com/nagise/archive/2007/09/12/95750.aspx</link><pubDate>Wed, 12 Sep 2007 20:51:00 GMT</pubDate><guid>http://blogs.wankuma.com/nagise/archive/2007/09/12/95750.aspx</guid><wfw:comment>http://blogs.wankuma.com/nagise/comments/95750.aspx</wfw:comment><comments>http://blogs.wankuma.com/nagise/archive/2007/09/12/95750.aspx#Feedback</comments><slash:comments>7</slash:comments><wfw:commentRss>http://blogs.wankuma.com/nagise/comments/commentRss/95750.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/nagise/services/trackbacks/95750.aspx</trackback:ping><description>&lt;p&gt;何を見てもアルゴリズムを考えてしまうのがプログラマの職業病第二段。&lt;br&gt;
&lt;a href="http://blogs.wankuma.com/nagise/archive/2007/08/05/88902.aspx"&gt;前回&lt;/a&gt;はナイトレイドという、
非常にマイナーな作品だったのですが、今回はメジャーなタイトルを取り上げてみたいと思います。&lt;/p&gt;

&lt;h4&gt;バトルガレッガの臨死&lt;/h4&gt;

&lt;p&gt;メジャーといいつつも世間的には非常にマイナーで申し訳ありません。&lt;br&gt;
90年代後半の弾幕シューティングの幕開けは怒首領蜂(どどんぱち 1997年 cave)とされることが多いですが、
BATTLE GAREGGA(バトルガレッガ 1996年 RAIZING)の2面ボスを見てcaveの開発者が
「そこまでやってもいいんだ」と弾幕をコンセプトにした怒首領蜂を作ったという逸話があり、
このバトルガレッガを&lt;strong&gt;弾幕シューティングの始祖&lt;/strong&gt;とする意見も多いです。&lt;/p&gt;

&lt;p&gt;シューティングゲームのスコアラー(スコアアタックをする人々)界隈では非常に有名なこのゲーム、
いくつかバグがあるのですが、&lt;strong&gt;初めてみたときに何が起こったのか理解できないのが「臨死」と呼ばれる現象&lt;/strong&gt;でしょう。&lt;/p&gt;

&lt;p&gt;本当は実物を見てもらいたいところなのですが、YouTubeで手ごろな動画が見つかりませんでした…。&lt;br&gt;
現象としては残機のない状態で、&lt;strong&gt;ミスをしながら1UPするとGameOver時にばらまくアイテムをばら撒いて死んだはずなのに
何事もなかったようにプレイ続行できる&lt;/strong&gt;、という現象です。&lt;/p&gt;

&lt;p&gt;バトルガレッガというゲームでは100万点ごとに1UPする上、スコアを稼ぐのにボムを大量に使用するので、
ゲームオーバー時に出るボムアイテムを回収しながらGameOverにならずにプレイ続行できるこの現象は、
スコアアタックでは非常に有利なのです。私も当時はこの「臨死」の練習をしたものです。&lt;/p&gt;

&lt;h4&gt;その原理&lt;/h4&gt;

&lt;p&gt;前提条件が幾つかあります。&lt;/p&gt;

&lt;p&gt;&lt;ul&gt;
&lt;li&gt;パワーアップなどがアイテム制で、ミスした際にはパワーアップアイテムなどをばら撒くタイプのゲームであること。
&lt;li&gt;ミスした際にその場で復活するタイプのゲームであること(グラディウスシリーズのような戻るタイプでは不可能)
&lt;li&gt;ミス中に1UPすることが可能であること
&lt;/ul&gt;&lt;/p&gt;

&lt;p&gt;最後の１機をミスをした瞬間にGameOverと表示させるのであれば面倒はないのですが、
ミスをした後にアイテムがばら撒かれ、自機が爆煙を上げて落ちるといった演出がされます。&lt;br&gt;
そのため、&lt;strong&gt;ミス時のアイテムの種類の判定はミスの瞬間に行い&lt;/strong&gt;(GameOver時には多くのアイテムをばら撒く)、
&lt;strong&gt;GameOver判定は次の自機が登場するタイミングで行う&lt;/strong&gt;というアルゴリズムが用いられます。&lt;/p&gt;

&lt;p&gt;そのため、アイテム判定時にはGameOverと判断され、コンティニュー用に多くのアイテムをばら撒き、
GameOverが確定するまでの間に1UPすることでGameOverとならずに復活することができるのです。&lt;/p&gt;

&lt;p&gt;開発もとのRAIZINGのアームドポリス バトライダー(1998年)では1UPをアイテム方式にして
この「臨死」が行えないようにしています。
臨死を行えるゲームはそれなりに多くあるようですが、あまりにシビアであったり、
また、臨死をするメリットがないので単なる大道芸となったりといった具合です。&lt;/p&gt;

&lt;p&gt;ゲームのようなリアルタイム性の強いプログラムでは、この現象のように思わぬタイミングで
処理が発生すると奇異な現象が起こることがあります。
プログラムする際にはこういった「間」にも注意を払いましょう。&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/nagise/aggbug/95750.aspx" width = "1" height = "1" /&gt;</description></item><item><dc:creator>凪瀬</dc:creator><title>ゲームのバグにみるアルゴリズム - 衝突判定</title><link>http://blogs.wankuma.com/nagise/archive/2007/08/05/88902.aspx</link><pubDate>Sun, 05 Aug 2007 18:28:00 GMT</pubDate><guid>http://blogs.wankuma.com/nagise/archive/2007/08/05/88902.aspx</guid><wfw:comment>http://blogs.wankuma.com/nagise/comments/88902.aspx</wfw:comment><comments>http://blogs.wankuma.com/nagise/archive/2007/08/05/88902.aspx#Feedback</comments><slash:comments>39</slash:comments><wfw:commentRss>http://blogs.wankuma.com/nagise/comments/commentRss/88902.aspx</wfw:commentRss><trackback:ping>http://blogs.wankuma.com/nagise/services/trackbacks/88902.aspx</trackback:ping><description>&lt;p&gt;何を見てもアルゴリズムを考えてしまうのがプログラマの職業病といったところです。&lt;br&gt;
私は結構ゲーム好きなんですが、ついついアルゴリズムを考えてしまうんですね。
バグを発見したときなどは楽しくてたまりません。
そのほつれからプログラムのコードが透けて見えるのです。&lt;/p&gt;

&lt;p&gt;本日のターゲットは2001年、匠から発売されたアーケードゲーム「ナイトレイド」です。
ジャンルはシューティングなのですが、変なシステムを採用した、あまり見栄えのしないゲームでした。
このゲーム、非常にマイナーです。多分、名前を聞いて分かる人の方が少数派ですね。
ちなみにプレイステーションに移植されています。&lt;br&gt;
&lt;a href="http://www.takumi-net.co.jp/products_arcade/shooting_nightraid/ac_night.htm"&gt;アーケード版公式ページ&lt;/a&gt;&lt;br&gt;
&lt;a href="http://www.takumi-net.co.jp/products_consumer/cons_night/cons_night.htm"&gt;PS版公式ページ&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;このゲームには非常に致命的なバグがあり一部界隈では有名です。&lt;br&gt;
&lt;strong&gt;自機を画面左上に移動させると敵が弾を撃たなくなるのです！&lt;/strong&gt;&lt;br&gt;
なぜこんなことが起こるのでしょうか？&lt;/p&gt;

&lt;h4&gt;矩形の衝突判定&lt;/h4&gt;

&lt;p&gt;ところで矩形(ようするに四角形のこと。プログラム用語。「くけい」と読む)同士の衝突判定はどうやるか分かりますか？&lt;/p&gt;

&lt;img src="http://nagise.wankuma.com/image/collision.png" alt="矩形の衝突判定" /&gt;&lt;br&gt;

&lt;p&gt;図のようにA,Bふたつの矩形があるとします。
Aの左上角の座標から見て右下にBの右下角があり(図の黒矢印)、
Aの右下角の座標から見て左上にBの左上角がある(図の赤矢印)場合にふたつの矩形は重なっているのです。&lt;/p&gt;

&lt;code&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;矩形Aの座標&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;(ax1,&amp;nbsp;ay1)───────────┐&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└───────────(ax2,&amp;nbsp;ay2)&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;矩形Bの座標&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;(bx1,&amp;nbsp;by1)───────────┐&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└───────────(bx2,&amp;nbsp;by2)&lt;/font&gt;&lt;br /&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;if&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;(((&lt;/font&gt;&lt;font color="#000000"&gt;ax1&amp;nbsp;&amp;lt;&amp;nbsp;bx2&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;amp;&amp;amp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;ay1&amp;nbsp;&amp;lt;&amp;nbsp;by2&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;amp;&amp;amp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;((&lt;/font&gt;&lt;font color="#000000"&gt;ax2&amp;nbsp;&amp;gt;&amp;nbsp;bx1&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;amp;&amp;amp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;ay2&amp;nbsp;&amp;gt;&amp;nbsp;by1&lt;/font&gt;&lt;font color="#000000"&gt;))){&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#3f7f5f"&gt;//&amp;nbsp;衝突！&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;

&lt;p&gt;シューティングゲームでは敵に近くから弾を撃たれて即死しないように配慮して、
敵が自機に近い場合は弾を撃たないようにしているものがあります(ゲーマー用語で弾封じといいます)。&lt;/p&gt;

&lt;p&gt;このナイトレイドでは自機と敵が重なってもミスとはならない仕様なのですが、
重なった状態から弾を撃たれると理不尽だろうという配慮でしょうか、
この弾封じの判定が実装されているわけです。&lt;/p&gt;

&lt;p&gt;そして、あろうことか、弾封じのための矩形の衝突判定で条件式の&amp;quot;&amp;lt;&amp;quot;と&amp;quot;&amp;gt;&amp;quot;を
間違えるというコーディングミスをしてしまったのです！&lt;/p&gt;

&lt;code&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;矩形Aの座標&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;(ax1,&amp;nbsp;ay1)───────────┐&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└───────────(ax2,&amp;nbsp;ay2)&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;矩形Bの座標&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;(bx1,&amp;nbsp;by1)───────────┐&lt;/font&gt;&lt;br /&gt;
&lt;font color="#3f7f5f"&gt;//&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;└───────────(bx2,&amp;nbsp;by2)&lt;/font&gt;&lt;br /&gt;
&lt;font color="#7f0055"&gt;&lt;b&gt;if&amp;nbsp;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;(((&lt;/font&gt;&lt;font color="#000000"&gt;ax1&amp;nbsp;&amp;lt;&amp;nbsp;bx2&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;amp;&amp;amp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;ay1&amp;nbsp;&amp;lt;&amp;nbsp;by2&lt;/font&gt;&lt;font color="#000000"&gt;))&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;amp;&amp;amp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;((&lt;/font&gt;&lt;font color="#000000"&gt;ax2&amp;nbsp;&lt;/font&gt;&lt;font color="#ff5050"&gt;&lt;b&gt;&amp;lt;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;nbsp;bx1&lt;/font&gt;&lt;font color="#000000"&gt;)&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;amp;&amp;amp;&amp;nbsp;&lt;/font&gt;&lt;font color="#000000"&gt;(&lt;/font&gt;&lt;font color="#000000"&gt;ay2&amp;nbsp;&lt;/font&gt;&lt;font color="#ff5050"&gt;&lt;b&gt;&amp;lt;&lt;/b&gt;&lt;/font&gt;&lt;font color="#000000"&gt;&amp;nbsp;by1&lt;/font&gt;&lt;font color="#000000"&gt;))){&lt;/font&gt;&lt;br /&gt;
&lt;font color="#ffffff"&gt;&amp;nbsp;&amp;nbsp;&lt;/font&gt;&lt;font color="#3f7f5f"&gt;//&amp;nbsp;衝突？&lt;/font&gt;&lt;br /&gt;
&lt;font color="#000000"&gt;}&lt;/font&gt;&lt;/code&gt;

&lt;p&gt;そして、誰もこのことに気づかないままゲームは出荷されたのでした。境界値テストは重要ですね。&lt;/p&gt;&lt;img src ="http://blogs.wankuma.com/nagise/aggbug/88902.aspx" width = "1" height = "1" /&gt;</description></item></channel></rss>