▲68通信vol.1のページへ


「読む前の注意点」
この記事は平成7年11月の学園祭直前に書かれたもので、 最新のTSH4と違う点があります。 ついでに今更ながら自分で読み返して言うと、 僕が文章を書く才能が無いのと、〆切直前で慌てて書いた 為に、怪しい文章がだらだらと続いています。 特に始めの「説教…」」と「??…」はつまらないので 読み飛ばすことをお勧めします。 それでは、お粗末ながら以下の記事を御覧下さい。
TSH ver.4.0 制作日記


「説教じじい」

 いきなりですが、「人が猿と違う点は何か」と聞かれたら どうしますか?
 僕は人間は色々な物を作り、文明を作り、文化を作るところ が違うと考えています。自分が欲しい物を作る。 そのために必要な物をまず作り、気に食わないところは 改良してまた作り直す。その試行錯誤を繰り返していると、 今度は不思議とほかの人も欲しがる物が出来上がるものです。 つまり、「まず自分が欲しいものを作る」ことは、 自分の為だけにやっているように見えるかもしれないが、 実はいつのまにか周りの人に幸せを与えることになっている ものです。
 というわけで皆さん、文化を作ってゆく人間として 「自分が欲しいもの」を作ろうじゃありませんか。 (別に物を作らない人は猿と同じだとは言っていないから 勘違いしないでね。それぞれの人間は考え、悩み、そうして 行動した結果として文化が作られてゆくのだから。)


「??、息切れ、立ちくらみ」

 横スクロール型シューティングゲームTSH4( TSH ver.4.0 ) の制作過程を書いてみます。
 まずはTSH3からTSH4に大きくバージョンアップ した理由ですが、TSH3を作ったときに設計した キャラクターを管理する構造体の設計が甘かった為、 色々と出来ない点が出てきたのです。 基本的にキャラクターの当たり判定は一つしか持てなかった ので、長方形の当たり判定を縦や横に伸ばすことしか 出来なかったのです。
 とりあえず違うキャラを強引に重ねて見せかけ上はなんとか ボスは誤魔化しましたが、内部的には汚いプログラムに なってしまいました。
 そこで、今回は始めから幾つかのキャラをひとつの 敵キャラとして扱い易いように、キャラ同士のポインタを 持たせ、また、エネルギーの共有化もさせてみました。 ちょっとしたことでしたが、その当時すでにCのプログラムの くせにスパゲッティになっていたTSH3を書き換えるくらい なら、新しく作り直したほうが早かったのです。 また、TSH3を作ったのはX68を友達から買って とりあえずの手慣らしの意味でもあったので、 まずは「動けばいい」で作っていたからプログラム自体は MSX−Cの頃と余り変わらなく、makefile を上手に 使っていなかったりとか、まあ何にしろ汚いプログラムだった と思います。


「制作日記・設計」

 まずは操作系ですが、例によってジョイスティックレバーで 移動、Aボタンで通常弾の発射、Bボタンでロックオンビーム の発射(出る予定、原稿を書いている時点でははまだ出てない )です。TSH3でついていたボンバーは使う人が少なかった (僕の周りにボンバー嫌いが多かったのもある)ので やめました。
 次に敵キャラクターなどを動かす構造体を設計します。 このゲームを作る上でいちばん大切な所で、ここさえ上手に 設計できれば何でも出来てしまい、かつ、下手に作ると 後々後悔する場所です。
 TSH3での反省を生かして敵キャラクターを操作する構造体 CH_CTRL と、当たり判定の大きさなどのどのキャラでも 共通となる敵の種類ごとのデータを入れる構造体 CH_DATA を設計 してみます。
 そのまま乗せるにはだらだらしているので、重要な要点だけを 抜き出しておきました。
typedef struct {
	void	(*go)();	// 移動ルーチン
	void	(*put)();	// スプライト表示ルーチン
	void	(*hit)();	// 自機に撃たれた
	void	(*init)();	// 初期化ルーチン
	short	hx, hy;		// 当たり判定
	int		ax, ay,		// 加速力
			vx, vy;		// 最高速
	int	energy;			// 登場時のエネルギー
	short	hb;			// 打ち返し弾の数
	short	*sp;		// スプライト配列
	short	score;		// 得点
} CH_DATA;

typedef struct ref {
  int
	x, y,		// 位置(画面座標*DOT)
	vx, vy,		// 速度()
	ax, ay;		// 加速度()
  int
	*lnke,		// リンクエナジー:普通は自分のenergyを示す
	energy;		// 残りエネルギー
  CH_DATA *dat;	// 敵機のデータテーブルのポインター
  struct ref *frm;	// 敵同士のポインター
} CH_CTRL;

(以下、説明の便宜のために CH_DATA dat; CH_CTRL ch; ch.dat=&dat;としておきます。)
 CH_DATA でのミソだと思うのは "void (*go)();" などの関数への ポインタでしょう。ゲームをたち上げ、敵のプログラムに 初期設定する時に関数へのポインタを入れ ( void move_zako_a(CH_CTRL*); ch[0].go=move_zako_a;)ます。 これは put,hit,init も同様にします。go は敵キャラを移動させる ルーチンで、種類ごとに関数を用意しておきます。 色違いの敵などは次の CH_DATA で sp だけ変えてやれば すぐにできます。
 put はスプライトを置いてやる(実際にはタイマ割り込みが入る まではバッファに溜めている)関数へのポインタで、 ch.vx,ch.vy などを見ながら ch.dat->sp から 選んだスプライト(ch.dat->sp[2]とか)を置くものです。 hit は敵がダメージを食らった時の反応をさせる関数へのポインタ を入れます。
 弾のダメージを受けないものとか、ダメージを受けると 膨らんでゆくものとかの表現は、こういった部分を関数にして おくことで他にも色々なことが出来るので非常に便利です。 init ではその種類の敵がどんな場所から登場させるかとか、 必要ならばもう幾つかの CH_CTRL を要求してリンクさせたり します。
 僕の場合は自機の位置を見て、出現と同時に撃ち落とされない 位置にばらまいて出現させています。あとは特に問題は ないでしょう。

 CH_CTRL でのポイントは、まずはTSH3での反省から 追加された int *lnke です。
これは敵が自機の弾に当たった時に、そのダメージを何処に 影響させるかです。例えば羽のあるボスキャラが、 羽を撃たれてもダメージとして加算される時には、羽が ダメージを食らった時に頭のエネルギーを引きます。 (ボスの登場時に( wing.lnke=&head.energy; head.lnke=&head.energy)と 初期化して、ダメージを食らったら( *wing.lnke-=弾のダメージ) とすれば、羽に与えられたダメージも頭に与えられた ダメージも同じことになる。)
 もうひとつのポイントは struct ref *frm です。 これにより敵同士が互いに座標を見ながら動くことが できます。一見、frm と lnke を一緒にできそうですが、 たとえば大型戦艦の小型砲台について考えると、砲台は戦艦に くっついて動きますが、ダメージの計算は別にしたい時などが あります。
 こういった場合にも対応できるので僕は別々にしました。


「設計日記・制作」

 この原稿を書いている時点ではまだ途中までしか出来てない ので、今までのところ苦労した点を書きます。
 まず、TSH3のときは256*256表示の画面で 作りましたが、アスペクト比がなんとなく気になって いましたので、TSH4にするにあたり思い切って 384*256に変えることにしたのです。が、、、 いきなり探してみると手もとに資料がない、しょうがないから 手計算でCRTコントローラのレジスタに送る値を弾き出して 試してみる。うまくいかない、それでもしつこく計算を 繰り返して、3、4日を無駄に送ってしまった。同好会で 聞いてみると、Oh!Xの付録に付いていたDoGAシステム になんか入っていたとのこと、さっそく家に帰って探し出し、 試してみるとあっさりと出来てしまった。
(HRLビットの意味を知らなかったんだもん。 しょうがないよな。黒本にも青本にも書いてなかったぞ! 片方だけ読んでもなんか足りないし、ばらばらの場所に 書いてあるから全部読まないとわからないし、かといって 全部読んでおけるほど暇じゃないぞ。MSXに比べると 必要になる資料がちゃんと整っていないよな。ちょっと怒り)
 さて、スプライトをうごかしてみる。!。スプライトが 右半分にいくと画面の右半分が化け化けになっていくぞ? なんじゃこりゃ?試行錯誤してプログラムのコンパイルを 繰り返す。コンパイルをしている間に青本とかに目を 通していると、「スプライト仕様 表示制限」が目にはいる。 要約すると実画面を256*256にするとBG面のサイズは 512*512になり、8*8サイズのパターンでBG面は 構成される。また、実画面を512*512にするとBG面の サイズは1024*1024となり、この場合は16*16 サイズのパターンでBG面は構成される。
 ここで僕が使用した画面は384*256表示画面なのだが、 これは512*256表示画面の変則画面であり、 実は基本的には512*256画面と同じことなのだった。 僕としてはBG面を1面使ってスコア表示などをやろうと していた為に、文字のサイズは8*8サイズであった。 このため、スプライトコントローラのレジスタは 256*256表示の設定がされていたのでした。 今回はちゃんと読んでいなかった僕も(素直には 認めないとこに意地がある?)悪いので、あきらめて TSH3と同様にテキスト画面に文字を出す。
 次に、実画面512*512のモードでPICファイルを 読もうとする。
 「モードが自動で変わっているなあ、スイッチ"/N"でとめてと、 、、」バグる。、、、
 hapic.r を使っているのだが、どうやら自動にすると 実画面1024*1024のモードにしたがるところを見ると 、ずばり実画面512*512のモードに絵を読めないと みたぞ。と、いうことはどうしよう、、、 ベタファイルを作ってみたが洒落にならない位でかいし (4枚で2Mになってしまう)、やはりPICが読めたほうが いいから、読み込む時は実画面1024*1024にして しまおう。
 あとはうまく実画面512*512の4枚になるように 変形コピーしてから、その画面を1024*1024で セーブしておけば良いだろう。よし、
 と、ここまでなんですねえ、TSH3で作ったことのある 部分はあっさりと出来上がってゆくのに、新しいものに 挑戦しようとするとえらく手間取る。X68でのノウハウが まだ出来てないからなんだろう。もうあとは当日にならないと 何処まで出来ているのかわからん。 いわゆる「神の味噌汁」ですね。


「ゲームを作ってほしいな」

 多分それぞれの人が、自分の理想とするゲームをたくさん 持っているはずです。でも、時間がないとか、いろんな 要因に阻まれて作れないでいるようです。
 ここでは僕がどうやってゲームを作ってゆくのかを ちょっと書いてみました。
 まずはいきなりだが「欲しいゲームとは何か」です。正しく 言い変えると「遊びたいゲームは何か」ですね。 僕の場合では初めて見たゲームの影響がとても強く、ゼビウス やグラディウスといったシューティングゲームがまず上がり ます。
 そういったわけで僕はシューティングを作ります。 次にもうちょっと具体化させてみます。自分が何故いまさら 沢山出ているシューティングを作るのかを考えてみると、 いままで出ているものに満足していない部分があるからです。 ここでちょっと気に触るものを上げてみると、 「かっくん曲がるのを止めろ」って言うのと、「知ってないと 死ぬってのは嫌」です。 まあ、「知ってないと死ぬ」というのは、ほとんど「かっくん 」を解決すれば済むことでしょう。
 ここをもうちょっと具体的な数字で表せるように考えて みましょう。
 「かっくん」とはつまり、慣性を無視した動きのことで、 敵や敵の行動データのなかに位置( x, y)しか保存されて いないために、行きたい速度を足すといきなり行きたい方向に 動いてしまうためと考えられます。 高校の物理によると、「まず力が加わり物が動きだす」と あるはずです。
 加速度( ax, ay)があり、速度( vx, vy)が発生( vx += ax, vy += ay) して、物体の位置( x, y)が変わる( x += vx, y += vy)のです。 ということは敵キャラの思考ルーチン中では加速度( ax, ay) しか操作出来なければ、敵が慣性を無視していきなり移動方向 を変えるということはなくなります。 あとはゲームのメインルーチンのなかで慣性を計算( vx += ax; vy += ay; x += vx; y += vy)してやるだけで「かっくん」は なくなります。
 これでもうこの部分だけはプログラムになってしまいました。 あとはゲームを完成させるためにほかの部分も同じように 造ってゆけばよいでしょう。
 こんな感じで僕はゲームのプログラムの動きを少しずつ 決めてゆきます。 とりあえずは、自分が動かすものから作ってゆく方が面白く 作ってゆけるでしょう。
 また僕の場合は、まず適当にノートに仕様を思いつくままに 書きなぐります。そうして、そのゲームの基本となる システムが固まったら、画面モード(解像度、色数、枚数)を 決めて、キャラの大きさを決定します。そうしたら今度は 動かす絵を描いてゆきます。
 今回の場合(TSH4)では自機、敵キャラ、自機の弾、敵の 弾、爆発パターンなどのスプライトで表示させる絵をまず描き ました。
 そうしたら今度は描いた絵のでーたをプログラムで読み込んで とりあえず表示させる部分を作ります。 うまく読めたのを確認したら、ちょっと自機のスプライトの 座標を for(x=0;x<255;++x) などとして動かしてみましょう。 次にはジョイスティックの倒された方向を読み込んで、それに あわせて自機を動かしてみます。 ここらからゲームのプログラムが面白くなって来るでしょう。 次に弾を撃たせ、敵を出して、自機にめがけて突っ込んでくる ようにします( en.ax = my.x - en.x < 0 ? -1: 1; en.ay = my.y - en.y < 0 ? -1: 1)。 これが出来たら敵と自機の弾の判定をしてみて、当たったら 敵を消します。
 このようにして、まず自分が操作して画面に反映される部分 から少しずつ作ってゆけば、楽しくゲームのプログラムを 進めてゆくことができます。
 皆さんも自分の持つ想像力(妄想とは違うっす)を育てて、 それを実現するために一つ一つ作ってゆきましょう。 いつかそのプログラムに出会えることを楽しみにしています。

「若年よりの昔話」

 ついに僕の作る横スクロール型シューティングゲームも 4作目になった。思い起こせば、初めて作ったゲームは友達が 移植したテトリスに刺激されてMSX2でC言語を覚えながら 作ったコラムスであった。
 その次にはまたその友達が縦シューを作り、ライバル意識に 燃えて作ったのがMSX1での横スクロールシューティング ゲームのTSHであった。
 因みに名前の由来はというと「T」は簡易版を気楽に作る意味 での「Tiny」の「T」と、「narusa−t」の「t」 を掛けている。「SH」はそのまま「SHooting」の 略で「SH」です。
 このTSHは基本的にベーシックで書かれ、割り込み関係だけ は必要に迫られてマシン語を使用した。ベーシックプログラム は「べーしっ君」というBASICコンパイラを通して 動いていた。これはBASICプログラムの途中に"CALL  TURBO"という魔法の一行を書き込むだけで、 そこから後ろがマシン語で動いてくれるという素晴らしく 便利なものだった。もしバグが出たら_TURBOの行を REM文(Cで言うコメントアウトね)にして、 おかしな動きをする場所まで実行させてBREAKする。 そこでおもむろに怪しい変数をすべてプリントして 予定どうりの動きかどうかをチェックし、バグの原因を 推測してみる。
 僕のこの変数をプリントして推測してというプログラムの 作り方は未だに変わらないが、高性能で余裕のあるメモリを 持ち、高機能なデバッガが用意されている最近のプログラム 環境に比べて考えてみると、今となってはとっても原始的だと 思う。まあそれは置いといて、そのTSHは親友等の間では 予想以上に評判も良く(悪い点は聞き流し)、今になって 思うと、その時に気分を良くしたのが原因で、いつのまにか ゲーム作りにハマってしまったようだ。
 僕はその後、もう少し本格的にしたTSH2を作り始めるが 、途中まで出来たところでどうにもならないバグに ぶつかってしまい、TSH2は幻の名作(?)となったので ある。
 そんなわけで、僕にはMSXで作れなかった「僕の遊びたい 横シュー」を作らなければ、という義務感がなんとなしに あるようだ。
 僕がいまだに横シューにこだわるのはまだ理由があって、 もともとシューティングゲームが好きだというのと、 テレビが横画面だということから「横」「シュー」になるので ある。
 本当はドラゴンスピリット(スピリッツ?)やレイフォースと いった縦シューも作りたいんだけど、やるたんびにテレビを 縦にするのはさすがに面度だし、さらに上手なモニターの 両端の処理が思いつかないし横シューができればまあいいや。
 さらに本当はシューティングだけでなく、もっといままでに なかったゲームとかを作りたいんだけど、それはまだ創造力が 足りないせいで、なかなかゲーム(プログラム)の形に ならない。これは面白くなるというアイデアはあるのに、 その周りが固まらないのは悔しいものだ。
 まあ自分の腕が足りないのはしょうがないから他のゲームを 見ながらそのゲームがどういった処理しているのか考えながら 頭を鍛えたり、仕事でプログラムを作ったり他の人の作った プログラムを読んだりして、その合間に少しずつでも自分が 何を求めているのかを考えてみたりしている。
 いつか皆で面白い作品を持ち寄って集まる日を楽しみにして います。では、またの機会に会いましょう。

narusa-t@jed.uec.ac.jp , narusa-t@maron.cs.uec.ac.jp
▲68通信vol.1のページへ